简体   繁体   English

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

[英]Making multiple queries using JPA, Spring, Hibernate

I have recently configured my first JPA, Spring and Hibernate app to manage Users and Roles. 我最近配置了我的第一个JPA,Spring和Hibernate应用程序来管理用户和角色。

I have used The crudRepository to create my queries. 我已使用CrudRepository创建查询。 I have two Repositories, one for Users, and one for Roles. 我有两个存储库,一个用于用户,一个用于角色。

Initially I Autowired my repositories to a Controller, then called them like this: 最初,我将存储库自动连接到Controller,然后将其命名为:

Role godRole = roleRepository.findByName(roleName);
User newUser = new User("me@me.net", "mARK");

newUser.addRole(godRole);

userRepository.save(newUser);

This resulted in an exception: 这导致了一个例外:

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)

I assume this is happening because each query is run in a separate session, therefore the by the time I try to add the Role to the User, the role is n longer attached to a session and because it has an Id it tries to do an insert instead of matching it with an existing Role. 我认为发生这种情况是因为每个查询都在一个单独的会话中运行,因此,当我尝试将角色添加到用户时,该角色不再附加到会话中,并且因为它具有一个ID,因此尝试执行插入而不是与现有角色匹配。

I read that if the method carrying out the queries uses the @Transaction annotation that the queries would all run in the same session, this doesn't seem to have made a difference! 我读到,如果执行查询的方法使用@Transaction批注,则查询将全部在同一会话中运行,这似乎没有什么不同!

Can anyone she any light on how I should be trying to implement this behaviour, it seems like a fairly simple thing to want to be able to do! 任何人都可以对我应该如何实现这种行为有任何看法,想要做到这一点似乎很简单!

applicationContext.xml 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 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 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;
}

Regards mARK 关于mARK

Your controller method should be annotated with @Transactional. 您的控制器方法应使用@Transactional进行注释。 In your code, new transaction is created when you invoke repository method and is closed after method completion. 在您的代码中,新事务在您调用存储库方法时创建,并在方法完成后关闭。

Anyway, it's a bad practice to start transaction in controller. 无论如何,在控制器中启动事务是一个坏习惯。 It's better to create some service layer(transactional) between your controller and dao layers. 最好在控制器和dao层之间创建一些服务层(事务性)。

CascadeType.ALL means all operations made on the User object will be made on the Role object. CascadeType.ALL表示对User对象进行的所有操作都将在Role对象上进行。

In this case, SAVE operation is cascaded to object Role which is detached now. 在这种情况下,SAVE操作将级联到现在已分离的对象Role中。 That's why hibernate throw exception. 这就是为什么休眠抛出异常。

I'm curious why the Role object is being detached from session. 我很好奇,为什么将Role对象从会话中分离出来。 When you made method Transactional (default propagation.Requires so it should create new transaction if no exists) and get your Role you should still have the same session. 当您将方法设为事务性(默认传播。要求,因此如果不存在该方法则应创建新的事务)并获取角色时,您仍应具有相同的会话。

EDIT: I checked it on my project. 编辑:我检查了我的项目。 You can't save an entity with id (especially when you have @GeneratedValue annotation). 您无法保存具有ID的实体(尤其是当您具有@GeneratedValue批注时)。 So you can't cascade persist operation to Role entity. 因此,您无法将持久性操作级联到角色实体。 So removing CascadeType.ALL is the only solution IMO. 因此,删除CascadeType.ALL是IMO的唯一解决方案。 Of course you can set CascadeType.UPDATE, CascadeType.REMOVE and so on. 当然,您可以设置CascadeType.UPDATE,CascadeType.REMOVE等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM