hibernate - JPA。从 Hibernate 迁移到 Eclipselink 时的事务问题

标签 hibernate jpa glassfish eclipselink glassfish-3

标准 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 下表现完全不同呢?有任何想法吗?

更新

进一步的调查表明问题出在实体映射的某个地方。在我的例子中,提到的两个实体( UserImplDirectoryImpl )都是从单个根类继承的,如下所示:
@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 没有将数据插入用户表 (as10user),导致最后一次查询中的 FK 冲突。但我仍然不知道为什么会这样。

更新 2

这是异常(exception):
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 可能正在尝试解决导致错误的双向关系,您可能需要删除非空约束以允许插入到您的表中,或者使用定制器定义约束依赖关系,或者修复您的双向关系映射。

更新

发生错误是因为您将约束定义到子 User 表而不是定义了 Id 的父 Root 表。默认情况下,EclipseLink 会将写入延迟到没有引用的辅助表中,以优化事务并避免数据库死锁。

从技术上讲,EclipseLink 应该找到对 User 的引用而不是这样做,如果您使用的是最新版本,它可能已被修复,否则记录错误并投票。
您可以通过使用 DescriptorCustomizer 并在根描述符上设置属性 setHasMultipleTableConstraintDependecy(true) 来解决此问题。

此外,您似乎让所有类共享同一个 Root 表,该表似乎只定义了 id,这可能不是一个好主意。您最好将 Root 设为 @MappedSuperclass 并且只有具体子类的表。

关于hibernate - JPA。从 Hibernate 迁移到 Eclipselink 时的事务问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7738044/

相关文章:

java - 无法连接到cassandra : NodeContext killing all pooled connections for session

java - JPA 中 SUM 的最大值

java - 当使用 JPA Hibernate 将子项和父项存储在同一个表中时,在当前和所有后续父项中使用提供的值进行搜索

java - 需要简单的方法来强制所有 DynaBean 属性名称为小写

performance - Spring、JPA (Hibernate) 和 Ehcache 性能不佳

java - Glassfish 4.1 + Hibernate 5.2 连接

mysql - Hibernate criteriaQuery - 左连接

java - Hibernate @GenerateValue 尝试修改标识列

java - Eclipselink mongoDB native 查询 find() 不起作用

java - Eclipse Glassfish 启动配置 - VM 参数被忽略