我最近配置了我的第一个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
答案 0 :(得分:0)
您的控制器方法应使用@Transactional进行注释。 在您的代码中,当您调用存储库方法时会创建新事务,并在方法完成后关闭。
无论如何,在控制器中启动事务是一种不好的做法。 在控制器和dao层之间创建一些服务层(事务性)会更好。
答案 1 :(得分:0)
CascadeType.ALL表示将在Role对象上对User对象进行的所有操作。
在这种情况下,SAVE操作级联到对象Role,该角色现在已分离。这就是hibernate抛出异常的原因。
我很好奇为什么将Role对象从会话中分离出来。当您创建方法Transactional(默认传播。所以它应该创建新事务,如果不存在)并获得您的角色时,您应该仍然拥有相同的会话。
编辑: 我在我的项目上检查了它。您无法保存具有id的实体(尤其是当您拥有@GeneratedValue注释时)。因此,您无法将持久操作级联到Role实体。 因此删除CascadeType.ALL是唯一的解决方案IMO。当然,您可以设置CascadeType.UPDATE,CascadeType.REMOVE等。