标准
Java EE环境:从JSF bean调用的JPA EJB.服务器:glassfish 3.1.1
使用Hibernate作为JPA提供程序开发,测试和部署的代码拒绝使用Eclipselink持久化实体 – glassfish中的默认JPA提供程序:
这是我从JSF bean调用的代码:
@Singleton
@Startup
public class SecurityService {
@PersistenceContext(unitName = "unit01")
private EntityManager em;
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public String createNewUser(String login) {
UserImpl newUser = new UserImpl();
newUser.setLogin(login);
em.persist(newUser);
//em.flush() ------> works fine when uncommented
DirectoryImpl userHome = new DirectoryImpl();
userHome.setOwner(newUser);
em.persist(userHome); // Throws exception due to FK violation
//
//
如评论所述,代码只有在持久化新用户后刷新EntityManager才有效,这显然是错误的.在测试其他方法时,我发现只有当我关闭服务器时,其他一些更改才会刷新到DB.
以上所有都发生在一个全新的GF安装中,纯Java EE设计中没有任何Spring或任何框架.
这是我的persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="unit01" transaction-type="JTA">
<jta-data-source>jdbc/Unit01DS</jta-data-source>
<properties>
<property name="eclipselink.logging.level" value="FINE" />
</properties>
</persistence-unit>
</persistence>
以下是我创建数据源的方法:
asadmin create-jdbc-connection-pool --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource --restype javax.sql.ConnectionPoolDataSource --property "User=u1:Password=xxx:URL=jdbc\:mysql\://localhost/dbname" Unit01DS
asadmin create-jdbc-resource --connectionpoolid Unit01DS jdbc/Unit01DS
这就是全部,没有使用其他配置文件/选项.那么为什么我的代码在Hibernate下工作正常并且在Eclipselink下表现完全不同?有任何想法吗?
UPDATE
进一步的调查表明,问题存在于实体映射中.在我的例子中,两个提到的实体(UserImpl和DirectoryImpl)都是从一个根类继承的,如下所示:
@Entity
@Table(name = "ob10object")
@Inheritance(strategy = InheritanceType.JOINED)
public class RootObjectImpl implements RootObject {
private Long id;
@Id
@Column(name = "ob10id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
//
UserImpl是根实体的直接子类,没有对其他实体的引用
@Entity
@Table(name = "as10user")
@DiscriminatorValue(value = "AS10")
public class UserImpl extends RootObjectImpl implements User {
private String login;
//
//
一个更复杂的案例是DirectoryImpl:
@Entity
@Table(name = "st20directory")
@DiscriminatorValue(value = "ST20")
public class DirectoryImpl extends AbstractStorageNode implements Directory {
// Inside there are also no references to other entities
其中AbstractStorageNode还扩展了根对象:
@Entity
@Table(name = "st10storage_node")
public abstract class AbstractStorageNode extends RootObjectImpl implements StorageNode {
private Set<AbstractStorageNode> childNodes;
private StorageNode parentNode;
private User owner;
@OneToMany(mappedBy = "parentNode")
public Set<AbstractStorageNode> getChildNodes() {
return childNodes;
}
@ManyToOne(targetEntity = AbstractStorageNode.class)
@JoinColumn(name="st10parent", nullable = true)
public StorageNode getParentNode() {
return parentNode;
}
@ManyToOne(targetEntity = UserImpl.class)
@JoinColumn(name = "as10id") // this is FK to `UserImpl` table.
public User getOwner() {
return owner;
}
// Setters omitted
现在这里是生成的sql:
// Creating user:
INSERT INTO ob10object (field1, field2, ob10discriminator) VALUES ('v1', 'v2', 'AS10')
SELECT LAST_INSERT_ID()
// Creating Directory:
INSERT INTO ob10object (field1, field2, ob10discriminator) VALUES ('v11', 'v22', 'ST20')
SELECT LAST_INSERT_ID()
INSERT INTO st10storage_node (st10name, as10id, st10parent, ob10id) VALUES ('home', 10, null, 11)
现在我看到会发生什么:eclipselink不会将数据插入User表(as10user),导致上次查询中出现FK违规.但我仍然不知道为什么会这样.
更新2
这是一个例外:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`dbname`.`st10storage_node`, CONSTRAINT `F_ST10_AS10` FOREIGN KEY (`as10id`) REFERENCES `as10user` (`ob10id`))
Error Code: 1452
最佳答案 那么,没有刷新你会得到一个约束错误?请包括异常和堆栈跟踪以及SQL日志,还包括如何映射关系以及如何定义ID.
你有双向关系吗? EclipseLink可能正在尝试解决导致错误的双向关系,您可能需要删除非空约束以允许插入表,或使用自定义程序定义约束依赖关系,或修复双向关系映射.
更新
发生错误的原因是您已将子用户表的约束定义为未定义Id的父根表.默认情况下,EclipseLink将写入保留为没有引用来优化事务并避免数据库死锁的辅助表.
从技术上讲,EclipseLink应该找到对User的引用,而不是这样做,你是否在最新版本,它可能已被修复,否则记录一个错误并投票给它.
您可以使用DescriptorCustomizer并在Root描述符上设置属性setHasMultipleTableConstraintDependecy(true)来解决此问题.
此外,您似乎让所有类共享相同的Root表,似乎只定义了id,这可能不是一个好主意.你可能最好使Root成为@MappedSuperclass并且只有具体子类的表.