使用JPA,Spring,Hibernate进行多个查询

时间:2015-01-29 11:31:32

标签: java spring hibernate spring-mvc jpa

我最近配置了我的第一个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

2 个答案:

答案 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等。