2011-10-12 94 views
4

标准的Java EE环境:JPA + EJB从JSF豆调用。服务器:GlassFish的3.1.1JPA。交易问题,而从休眠迁移到EclipseLink的

守则,开发,测试和部署Hibernate作为JPA提供者,拒绝与EclipseLink的坚持实体 - 在GlassFish中的默认JPA提供商:

下面是我从JSF豆调用代码:

@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的代码才起作用。在测试其他方法时,我发现只有在关闭服务器时才会将其他更改刷新到数据库。

以上所有发生在一个新的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

进一步的调查表明,问题就出在实体映射的地方。在我的情况都提到实体(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 

WHE再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 [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 
+0

我也有同样的问题。看起来Hibernate JPA持久性提供程序缓存体系结构与EclipseLink不同,但我找不到解决方法。这里有一篇有趣的文章,可能有助于... http://blogs.oracle.com/carolmcdonald/entry/jpa_caching – perissf

+0

@perissf,谢谢你的文章。总是认为缓存是规范背后的事情,如果供应商不违反规范,供应商可以自由使用任何缓存机制。我稍微被我在这里得到的东西嗤之以鼻,我坚信我错过了一些配置选项。 – Osw

回答

2

所以,不要冲洗你得到一个约束的错误?请包括异常和堆栈跟踪和SQL日志,还包括你是如何映射关系,你是如何定义的ID。

你有双向关系吗? EclipseLink可能试图解决导致错误的双向关系,您可能需要移除非空约束以允许插入到表中,或者使用定制器定义约束依赖关系,或者修复双向关系映射。

更新

的错误发生,因为你必须定义为儿童用户表不是其中ID定义父根表上的约束。默认情况下,EclipseLink会阻止写入没有引用来优化事务并避免数据库死锁的辅助表。

技术上的EclipseLink应该在最新的版本中找到参考的用户,而不是这样做,你,它可能已被固定,否则记录错误,并投赞成票。 您可以通过使用DescriptorCustomizer并在根描述符上设置属性setHasMultipleTableConstraintDependecy(true)来解决此问题。

此外,您似乎已将所有类都共享相同的根表,似乎只定义了id,这可能不是一个好主意。你可能会更好地使Root成为@MappedSuperclass并且只有具体子类的表。

+0

谢谢,问题更新。 – Osw

+0

非常感谢! 'setHasMultipleTableConstraintDependecy'解决了这个问题。我使用最新的eclipselink'2.3.0.v20110604-r9504',我的根类还定义了一些其他道具(创建/修改日期,远程IP等)。这就是说,你是否仍然认为这是一个错误或只是一个配置选项?是否有任何eclipse配置选项会覆盖或影响这些缓存插入而不实现特定于eclipse的接口? – Osw

0

我想你会发现两种方法实际上都遵守JPA 2规范。 http://download.oracle.com/auth/otn-pub/jcp/persistence-2.0-fr-eval-oth-JSpec/persistence-2_0-final-spec.pdf?e=1318430009&h=d3d726bc7bec224bd8f803aba90d1f13

参见第3.2.2节

3.2.2坚持实体实例

一个新的实体实例将成为双方通过调用它的persist方法或 级联持续经营管理和持续性。应用于实体X的坚持 操作的语义如下:

•如果X是新实体,则它将变为受管理的。实体X将在事务提交时或之前进入数据库或作为冲刷操作的结果......

进入数据库的实际时间是松散定义的。数据库提供外键的ID,因此需要了解数据。

调用flush可能觉得不好,但它应该在Hibernate中工作了。它将数据写入数据库,但不会为EntityManager将其删除,因此该实体仍处于连接状态。

希望这会有所帮助。

0

我想这将需要更多的细节(即发布您的实体代码),由于相当类似的代码在我的情况没有任何问题运行。如果你发现任何差异,只要看看并给出一个标志。

它的Glassfish 3.1.1的EclipseLink。

EJB代码:

@Stateless 
public class MyEJB { 

    @PersistenceContext 
    EntityManager em; 

    @TransactionAttribute(value = TransactionAttributeType.REQUIRED) 
    public void doSmth() { 
     UserData ud = new UserData(); 
     em.persist(ud); 

     Website w = new Website(); 
     w.setUser(ud); 

     em.persist(w); 

     System.out.println("!!!!!" + w); 
    } 
} 

实体NR 1:

@Entity 
public class UserData implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    public Long getId() { 
     return id; 
    } 

    @Override 
    public String toString() { 
     return "com.entities.UserData[ id=" + id + " ]"; 
    } 
} 

实体NR 2:

@Entity 
public class Website implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    @OneToOne(optional=false) 
    private UserData user; 

    public Long getId() { 
     return id; 
    } 

    public void setUser(UserData u){ 
     user = u; 
    } 

    public UserData getUser() { 
     return user; 
    } 

    @Override 
    public String toString() { 
     return "com.entities.Website[ id=" + id + ", " + user +" ]"; 
    } 
} 

的persistence.xml

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 
    <persistence-unit name="TransactionsPU" transaction-type="JTA"> 
     <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 
     <jta-data-source>jdbc/__default</jta-data-source> 
     <exclude-unlisted-classes>false</exclude-unlisted-classes> 
     <properties> 
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> 
     </properties> 
    </persistence-unit> 
</persistence> 

HTH。

+0

谢谢,刚刚更新了问题 – Osw

0

我认为问题可能在您的实体。例如,我的用户实体是这样的:

public class User implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @Basic(optional = false) 
    private String login; 
} 

通知的@Id@Basic注解。我没有看到您在UserImpl的属性中使用任何注释。

我建议你修改你的实体注释。

+0

UserImpl从RootObjectImpl派生id属性。检查getId-method中的注释。 –