Как сопоставить свойство unset со значением без получения исключения NullPointerException в Dozer

StackOverflow https://stackoverflow.com/questions/1407041

  •  05-07-2019
  •  | 
  •  

Вопрос

Используя бульдозер для отображения двух объектов, я имею:

/**
/* This first class uses the GXT (ExtJS) framework
**/
Class1 extends BaseModelData
{
    public int getId()
    {
        return (Integer)get("id");
    }

    public void setId(int id)
    {
        set("id", id);
    }

    // more properties
}

Class2
{
    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    // more properties
}

Если я не задам идентификатор в первом классе (вызвав class1.setId()), результатом будет исключение NullPointerException из Dozer.Я понимаю, что это правильно, так как get("id") было бы равно null.

Конечно, я могу решить эту проблему, установив проверку на null и вернув -1 или 0, или что-то еще.

Проблема в том, что тогда это становится ошибкой времени выполнения, а не ошибкой времени компиляции.Я бы предпочел решить этот вопрос правильно.

Теперь я прочитал в Документация бульдозера о том, что вы можете заставить его пропустить null, выполнив команду map-null="false", но я не смог заставить это сработать...

Есть какие-нибудь предложения?

Это было полезно?

Решение

Я полагаю, что проблема не в бульдозере, а в скрытой автоматической распаковке в вашем геттере:

   public int getId()
{
    return (Integer)get("id");
}

(Integer)get("id") неявно преобразуется в int, потому что тип возвращаемого значения вашего метода - "int".

Это сработает в большинстве случаев...ЗА исключением случаев, когда результат равен null, и в этом случае вы получаете исключение NullPointerException, потому что int никогда не может быть null.

Это приводит к скрытым исключениям NullPointerExceptions...Более подробная информация здесь: http://www.theserverside.com/blogs/thread.tss?thread_id=41731

Чтобы решить эту проблему, у вас есть несколько вариантов:

  • Если Class1 и Class2 на самом деле могут содержать нулевой идентификатор, вы хотите изменить свои методы получения / установки, чтобы получать / устанавливать целые числа вместо примитивных целых чисел.

  • Если оба Class1 и Class2 никогда не должны содержать нулевой идентификатор, и вы считаете это инвариантом класса, вы можете сохранить примитивный тип int в получателе / установщике и либо:

    • Убедитесь, что get("id") никогда не будет равен null, инициализировав его некоторым определенным значением (например, 0) в конструкторе) и убедившись, что ничто не может установить его равным null.
    • Или решите, что getId() вернет значение по умолчанию, если null, и добавьте проверку null в getter, как вы сказали.
  • Если Class1 может иметь нулевой идентификатор, а Class2 - нет, у вас должны быть средства получения и установки Class1, использующие целочисленный тип вместо примитива int, и вам следует создать dozer CustomConverter, который возвращает значение по умолчанию, когда исходное поле равно null.

С уважением


[РЕДАКТИРОВАТЬ] Вот тестовый код, который показывает, что Dozer игнорирует нули сопоставления, когда его просят:

src/com/test/dozer/Class1.java :

package com.test.dozer;

import com.extjs.gxt.ui.client.data.BaseModelData;

public class Class1 extends BaseModelData {

    // Notice the return type here: "Integer" and *not* int
    // Returning int throws a NullPointerException when get("id") is null!
    public Integer getId() {
        return (Integer) get("id");
    }

    public void setId(Integer id) {
        set("id", id);
    }

}

src/com/test/dozer/Class2.java :

package com.test.dozer;

public class Class2 {

    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

src/dozerMappingFile.xml :

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">

  <configuration>
    <stop-on-errors>true</stop-on-errors>
    <date-format>MM/dd/yyyy HH:mm</date-format><!-- default dateformat will apply to all class maps unless the class mapping explicitly overrides it -->
    <wildcard>true</wildcard><!-- default wildcard policy that will apply to all class maps unless the class mapping explicitly overrides it -->
  </configuration>

  <mapping map-null="false">
    <class-a>com.test.dozer.Class1</class-a>
    <class-b>com.test.dozer.Class2</class-b>
  </mapping>

</mappings>

src/com/test/dozer/DozerTest.java :

package com.test.dozer;

import java.util.Arrays;

import junit.framework.Assert;

import org.dozer.DozerBeanMapper;
import org.junit.Before;
import org.junit.Test;

public class DozerTest {

    private DozerBeanMapper mapper;

    @Before
    public void setUp() {
        mapper = new DozerBeanMapper(Arrays.asList("dozerMappingFile.xml"));
    }

    /**
     * Verifies that class1's id is mapped into class2's id when not null.
     */
    @Test
    public void testMappingWhenIdNotNull() {
        Class1 class1 = new Class1();
        class1.setId(1);
        Class2 class2 = new Class2();
        class2.setId(2);

        mapper.map(class1, class2);

        Assert.assertEquals(1, class2.getId());
    }

    /**
     * Verifies that class2's id is not set to null when class1's id is null.
     */
    @Test
    public void testMappingWhenIdIsNull() {
        Class1 class1 = new Class1();
        Class2 class2 = new Class2();
        class2.setId(2);

        mapper.map(class1, class2);

        Assert.assertEquals(2, class2.getId());
    }

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top