EclipseLink + JPA + 通用实体 + SINGLE_TABLE 继承
Posted
技术标签:
【中文标题】EclipseLink + JPA + 通用实体 + SINGLE_TABLE 继承【英文标题】:EclipseLink + JPA + Generic Entity + SINGLE_TABLE Inheritance 【发布时间】:2013-10-23 08:58:17 【问题描述】:我想知道是否可以在 JPA 中定义一个通用实体,例如我的 PropertyBase,派生出具体的实体类,例如 ShortProperty 和 StringProperty,并将它们与 SINGLE_TABLE 继承模式一起使用?
如果我尝试通过 EntityManager 提交新创建的 ElementModel 实例(请参阅 ElementModelTest),我总是会得到一个 NumberFormatException,即“值”无法正确转换为 Short。奇怪的是,如果我将下面的所有类都定义为我的测试用例类“ElementModelTest”的内部静态类,这似乎可行。
任何想法我需要改变才能使这项工作?
我正在使用 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>
【问题讨论】:
TL;DR - 您能否减少其他人为理解您的问题而必须投入的工作量?将代码简化为相关细节 - 没有 ctors,没有访问方法,只有基本属性,给每个文件一个单独的列表?欢迎来到 *** :) 感谢您的提示,对混乱感到抱歉;) 【参考方案1】:您的问题是 PropertyBase<T>
是一个实体,其字段 value
是可持久的。您的数据库需要将该字段类型映射到列类型,并且您有三个实体对于同一字段具有不同的 java 类型:PropertyBase<T>
是通用的,它本身不知道它的 value
字段是什么类型,StringProperty
说它是一个字符串,ShortProperty
说它是一个 Short。所以这是一个冲突。
为了克服这个问题,您将字段value
设为瞬态,并且对于PropertyBase<T>
的每个子类型(如StringProperty
),您可以定义一个具有不同名称的新持久属性,例如。
StringProperty
会有一个private String stringValue
,ShortProperty
会有一个private Short shortValue
,每个字段都会映射到不同的DB 列。
PS:我无法解释为什么当你制作所有课程时它会起作用static inner
。
【讨论】:
与此question 相关,原则上应该可以工作。 “正常”和“内部静态”方法的区别在于值列的类型,在第一种情况下为 NUMBER,在第二种情况下为 VARCHAR。 在那个相关问题中,您的PropertyBase<T>
将是 @MappedSuperclass
而不是带有 JPA 继承的 @Entity
,所以这是不同的,因为没有冲突。请注意,在那个单一答案中,不起作用的情况正是您的情况。你也试过我上面描述的方案吗?
你是对的,这是一种不同的情况,会导致两个不同的实体表,我的目标是将所有内容都放在一个表中(单表继承)。受您的方案的启发,我尝试了一些不同的方法,并从PropertyBase<T>
中删除了value
字段,并将相关的getter 和setter 方法标记为abstract
。在派生类ShortProperty
和StringProperty
中,我再次定义了value
字段,瞧,这似乎现在像内部静态方法一样工作。顺便说一句:我认为你的方案应该可行。我对列表类做了类似的事情。
更新:我的方法不起作用。由于底层常量数据库字段类型,我在来回保存浮点值时遇到了问题。所以,我按照最初建议的方案,但从 PropertyBase<T>
类中删除了 value
字段。以上是关于EclipseLink + JPA + 通用实体 + SINGLE_TABLE 继承的主要内容,如果未能解决你的问题,请参考以下文章
Java JPA(EclipseLink)如何在持久化实际实体之前接收下一个 GeneratedValue?
JPA eclipselink坚持没有主键但有两个外键的实体