简体   繁体   English

JPA多对一关系CascadeType行为

[英]JPA many-to-one relationship CascadeType behavior

I have 2 tables: User and Loan . 我有2个表: UserLoan User have 3 fields: id (PK), first_name and last_name . 用户具有3个字段: id (PK), first_namelast_name Loan table have field user_id that is foreign key to User table: Loan表的字段user_idUser表的外键: 在此处输入图片说明

The logic of my application: when I create new Loan object and set there a corresponding user it should create a new one for unique user or set a id of a user into user_id for existing user. 应用程序的逻辑:当我创建新的Loan对象并在其中设置相应的user它应该为唯一用户创建一个新用户,或者为现有用户将user id设置为user_id

For that purpose my database have a unique index on first_name , last_name . 为此,我的数据库在first_namelast_name上具有唯一索引。

The Loan class uses User with @ManyToOne relationship: Loan类使用具有@ManyToOne关系的User

public class Loan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "user_id")
    private User user;
... methods ...

When I add new user, everything is fine, it persists to db with new PK. 当我添加新用户时,一切都很好,它会使用新的PK持久保存到数据库。 But when I try to add an existing one I got and exception: 但是当我尝试添加一个现有的时,我得到了例外:

javax.servlet.ServletException: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'FIRST_LAST_NAME' defined on 'USER'.
Error Code: 20000

Again when I put @ManyToOne(cascade = CascadeType.MERGE) I'm able only to enter existing users, as only I pass a new one that is not persisted in DB I got an exception: 同样,当我输入@ManyToOne(cascade = CascadeType.MERGE)我只能输入现有用户,因为只有我通过了一个未持久存储在数据库中的新用户,我得到了一个例外:

javax.servlet.ServletException: org.springframework.dao.InvalidDataAccessApiUsageException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: User{id=null, firstName='a', lastName='a'}.;

@ManyToOne(cascade = CascadeType.ALL) didn't work as well. @ManyToOne(cascade = CascadeType.ALL)也不起作用。

UPDATE 更新

the code for insertion new Loan is: 插入新Loan的代码是:

user = userService.findByName(firstName, lastName);
        if (user == null) {
            user = new User(firstName, lastName);
        }
        loan.setUser(user);
        loanService.save(loan);

findByName() and save() methods are: findByName()save()方法是:

private EntityManager em;    
public User findByName(String firstName, String lastName) {
            TypedQuery<User> query = em.createQuery(
                    "SELECT u FROM User u WHERE u.firstName = :firstName " +
                            "AND u.lastName = :lastName", User.class)
                    .setParameter("firstName", firstName).setParameter("lastName", lastName);
            List<User> users = query.getResultList();
            if (!users.isEmpty()) {
                return users.iterator().next();
            } else {
                return null;
            }
        }

     public void save(User user) {
            if (user.getId() == null) {
                em.persist(user);
            } else {
                em.merge(user);
            }
        }

If you call Persist on Loan a) with cascade.Persist set on the user relationship and the referenced user exists, set you will get an exception if the User instance was not read in from the same EntityManager instance/context - You are reading it through the userService and then saving the loan in the loanService, so the context would have to be container managed and within the same transaction to work. 如果在级联上对Persist进行贷款a)上的Persist设置,并且所引用的用户存在,则如果未从同一EntityManager实例/上下文中读取User实例,则设置为您将获得异常-您正在通过以下方式读取它用户服务,然后将贷款保存在贷款服务中,因此必须对容器进行上下文管理,并且必须在同一笔交易中进行操作。

b) If cascade.PERSIST (or cascade.ALL) is not set and the reference user is new, you will get the "new object was found" exception. b)如果未设置cascade.PERSIST(或cascade.ALL),并且引用用户是新用户,则将收到“找到新对象”异常。

If you cannot perform the read of the user in the same EntityManager you are going to save the loan in, switching to using merge may help, as JPA should then check referenced entities and merge as appropriate. 如果您无法在要保存借项的同一EntityManager中执行用户的读取,则切换到使用合并可能会有所帮助,因为JPA随后应检查引用的实体并进行适当的合并。 You will then need to have the cascade.MERGE option set on the relationships for the merge to be applied to the User instance. 然后,您将需要在关系上设置cascade.MERGE选项,以将合并应用于用户实例。

Note that using merge is different from Persist in that what you passed in does not become managed by the context. 请注意,使用合并与Persist的不同之处在于,您传入的内容不会由上下文管理。 That means after the transaction commits, the entities passed in will still not have any primary key values set. 这意味着在事务提交后,传入的实体仍然不会设置任何主键值。 You may want to pass back the entity returned from Merge if you are going to continue to use the entities for any other operations. 如果您要继续将实体用于其他任何操作,则可能需要回传从合并返回的实体。

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

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