2011-10-12 93 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中的註釋。 –