Вопрос

I was wondering if it's possible in JPA to define a generic entity like in my case PropertyBase and derive concrete entity classes like ShortProperty and StringProperty and use them with the SINGLE_TABLE inheritance mode?

If I try to commit newly created ElementModel instances (see ElementModelTest) over the EntityManager I always get an NumberFormatException that "value" can't be properly converted to a Short. Strangely enough if I define all classes below as inner static classes of my test case class "ElementModelTest" this seems to work.

Any ideas what I need to change to make this work?

I'm using EclipseLink eclipselink-2.6.0.v20131019-ef98e5d.


public abstract class PersistableObject implements Serializable {

    private String id = UUID.randomUUID().toString();
    private Long version;

}


public abstract class PropertyBase<T> extends PersistableObject {

    private String name;
    private T value;

}


public class ShortProperty extends PropertyBase<Short> {
    ...
}


public class StringProperty extends PropertyBase<String> {
    ...
}


public class ElementModel extends PersistableObject {

    private StringProperty name = new StringProperty();
    private ShortProperty number = new ShortProperty();

}


public class ElementModelTest extends ModelTest<ElementModel> {

    @Test
    @SuppressWarnings("unchecked")
    public void testSQLPersistence() {

        final String PERSISTENCE_UNIT_NAME = getClass().getPackage().getName();
        new File("res/db/test/" + PERSISTENCE_UNIT_NAME + ".sqlite").delete();

        EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
        EntityManager em = factory.createEntityManager();

        em.getTransaction().begin();

        for (int i = 0; i < 10; ++i) {
            ElementModel device = new ElementModel();
            device.setName("ElementModel: " + i);
            device.setNumber((short) i);
            em.persist(device);
        }

        em.getTransaction().commit();
        em.close();

    }

}


<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">

    <mapped-superclass
        class="PersistableObject">
        <attributes>
            <id name="id">
                <column name="id" />
            </id>
            <version name="version" access="PROPERTY">
                <column name="version" />
            </version>
        </attributes>
    </mapped-superclass>

    <entity class="PropertyBase">
        <table name="PropertyBase" />
        <inheritance />
        <discriminator-column name="type"/>
        <attributes>
            <basic name="name">
                <column name="name" />
            </basic>
            <basic name="value">
                <column name="value" />
            </basic>
        </attributes>
    </entity>

    <entity class="StringProperty">
        <discriminator-value>StringProperty</discriminator-value>
    </entity>

    <entity class="ShortProperty">
        <discriminator-value>ShortProperty</discriminator-value>
    </entity>

    <entity class="ElementModel">
        <table name="ElementModel" />
        <inheritance />
        <discriminator-column name="type"/>
        <attributes>
            <one-to-one name="name">
                <join-column name="name" referenced-column-name="id" />
                <cascade>
                    <cascade-all />
                </cascade>
            </one-to-one>
            <one-to-one name="number">
                <join-column name="number" referenced-column-name="id" />
                <cascade>
                    <cascade-all />
                </cascade>
            </one-to-one>
        </attributes>
    </entity>

</entity-mappings>
Это было полезно?

Решение

Your problem is that PropertyBase<T> is an entity with field value being persistable. Your database needs to map that field type to a column type, and you have three entities that have different java types for the same field: PropertyBase<T> is generic and has itself no idea what type its value field is, StringProperty says it is a String and ShortProperty says that is a Short. So that is a conflict.

In order to overcome this problem, you make field value transient, and for every subtype of PropertyBase<T> (like StringProperty) you can define a new persitable property with different names, eg. StringProperty will have a private String stringValue, ShortProperty will have a private Short shortValue, and every field will be mapped to different DB column.

PS: I cannot explain why it works when you make all the classes static inner.

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