[英]Spring Data JPA/Hibernate - using EntityManager for queries
[英]Making multiple queries using JPA, Spring, Hibernate
我最近配置了我的第一個JPA,Spring和Hibernate應用程序來管理用戶和角色。
我已使用CrudRepository創建查詢。 我有兩個存儲庫,一個用於用戶,一個用於角色。
最初,我將存儲庫自動連接到Controller,然后將其命名為:
Role godRole = roleRepository.findByName(roleName);
User newUser = new User("me@me.net", "mARK");
newUser.addRole(godRole);
userRepository.save(newUser);
這導致了一個例外:
org.hibernate.PersistentObjectException: detached entity passed to persist: uk.org.rspca.secrets.entities.Role
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:137)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
我認為發生這種情況是因為每個查詢都在一個單獨的會話中運行,因此,當我嘗試將角色添加到用戶時,該角色不再附加到會話中,並且因為它具有一個ID,因此嘗試執行插入而不是與現有角色匹配。
我讀到,如果執行查詢的方法使用@Transaction批注,則查詢將全部在同一會話中運行,這似乎沒有什么不同!
任何人都可以對我應該如何實現這種行為有任何看法,想要做到這一點似乎很簡單!
applicationContext.xml中
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="uk.co.secrets" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/DBPool" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<data:repositories base-package="uk.co.secrets.repositories" />
User.java
@Entity
@Table(name="S_User")
public class User {
@Id
@Column(name="u_pk_id")
//@GeneratedValue(strategy=GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "S_User_Seq")
@SequenceGenerator(name = "S_User_Seq", sequenceName = "S_User_Seq", allocationSize = 1)
private long id;
@Column(name="u_username")
private String username;
@Column(name="u_name")
private String name;
@Column(name="u_last_login")
private Date lastLoggedin;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "S_UserRole", joinColumns = {
@JoinColumn(name="u_fk_id") },
inverseJoinColumns = { @JoinColumn(name = "r_fk_id") })
private Set<Role> userRoles = new HashSet<Role>();
}
Role.java
@Entity
@Table(name="S_Role")
public class Role {
@Id
@Column(name="r_pk_id")
//@GeneratedValue(strategy=GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "S_Role_Seq")
@SequenceGenerator(name = "S_Role_Seq", sequenceName = "S_Role_Seq", allocationSize = 1)
private long id;
@Column(name="r_name")
private String name;
@Column(name="r_desc")
private String description;
@Column(name="r_created")
private Date dateCreated;
@Column(name="r_modified")
private Date dateModified;
@Column(name="r_removed")
private Date dateremoved;
}
關於mARK
您的控制器方法應使用@Transactional進行注釋。 在您的代碼中,新事務在您調用存儲庫方法時創建,並在方法完成后關閉。
無論如何,在控制器中啟動事務是一個壞習慣。 最好在控制器和dao層之間創建一些服務層(事務性)。
CascadeType.ALL表示對User對象進行的所有操作都將在Role對象上進行。
在這種情況下,SAVE操作將級聯到現在已分離的對象Role中。 這就是為什么休眠拋出異常。
我很好奇,為什么將Role對象從會話中分離出來。 當您將方法設為事務性(默認傳播。要求,因此如果不存在該方法則應創建新的事務)並獲取角色時,您仍應具有相同的會話。
編輯:我檢查了我的項目。 您無法保存具有ID的實體(尤其是當您具有@GeneratedValue批注時)。 因此,您無法將持久性操作級聯到角色實體。 因此,刪除CascadeType.ALL是IMO的唯一解決方案。 當然,您可以設置CascadeType.UPDATE,CascadeType.REMOVE等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.