@OneToOne从两个班级到同一班级

时间:2022-09-11 16:49:02

For better understanding, I want to achieve this:

为了更好地理解,我想实现这个目标:

@OneToOne从两个班级到同一班级

Note: Buyer may not have ExternalAccount but Seller must have it. What I have/tried:

注意:买方可能没有ExternalAccount,但卖方必须拥有它。我有/尝试过:

Buyer Class:

@Entity
public class Buyer extends User {

    @OneToOne(optional=true, cascade= {CascadeType.MERGE})
    @JoinColumn(nullable=true)
    private ExternalAccount externalAccount;
    //getters and setters

}

Seller Class:

@Entity
public class Seller extends User {

    @OneToOne(optional=false, cascade= {CascadeType.MERGE})
    @MapsId
    @JoinColumn(nullable=false)
    private ExternalAccount externalAccount;
    //getters and setters and other properties

} 

ExternalAccount class:

@Entity
public class ExternalAccount {

    @Id
    @PrimaryKeyJoinColumn
    private Long id;
    //getters and setters

}

I am using Spring Data JPA with Spring Boot and I want that:

我使用Spring Data JPA和Spring Boot,我想要:

  • If there's no Buyer related but ExternalAccount exists (associated with Seller), associate it.

    如果没有与买方相关但存在ExternalAccount(与卖方相关联),请将其关联。

  • If there's no Seller related but ExternalAccount exists (associated with Buyer), associate it.

    如果没有与卖方相关但存在ExternalAccount(与买方关联),请将其关联。

  • If no ExternalAccount exists, when saving Buyer/Seller, creates the ExternalAccount.

    如果不存在ExternalAccount,则在保存买方/卖方时,创建ExternalAccount。

I could achieve similar behavior with CascadeType.MERGE (after reading a lot of posts of *), but using this it doesn't respect @OneToOne mapping. It allows to create a lot of Buyers related to the same ExternalAccount.

我可以用CascadeType.MERGE实现类似的行为(在阅读了很多*的帖子之后),但是使用它时它不尊重@OneToOne映射。它允许创建许多与相同ExternalAccount相关的买家。

  • I've created a github project with the database tests to reproduce the issue.
  • 我已经用数据库测试创建了一个github项目来重现这个问题。

https://github.com/ralphavalon/jpa-mapping

There, I have my example rest controllers (MappingController):

在那里,我有我的示例休息控制器(MappingController):

//Creating buyer example
@RequestMapping(value = "/newBuyer", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Object newBuyer() {
    Buyer buyer = new Buyer();
    buyer.setBirthdate(LocalDateTime.now());
    buyer.setEmail("buyer@email.com");
    buyer.setName("Buyer Name");
    ExternalAccount external = new ExternalAccount();
    external.setId(123L);
    buyer.setExternalAccount(external);
    buyerDao.save(buyer);
    return buyer;
}

//Creating seller example
@RequestMapping(value = "/newSeller", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Object newSeller() {
    Seller seller = new Seller();
    seller.setBirthdate(LocalDateTime.now());
    seller.setEmail("seller@email.com");
    seller.setName("Seller Name");
    ExternalAccount external = new ExternalAccount();
    external.setId(123L);
    seller.setExternalAccount(external);
    sellerDao.save(seller);
    return seller;
}

When I call /newBuyer at the first time, it saves. Now, if I call /newSeller after calling /newBuyer it returns this:

当我第一次打电话给/ newBuyer时,会保存。现在,如果我在调用/ newBuyer之后调用/ newSeller它会返回:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PRIMARY KEY ON PUBLIC.EXTERNAL_ACCOUNT(ID)"; SQL statement:
insert into external_account (id) values (?) [23505-196]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.EXTERNAL_ACCOUNT(ID)"; SQL statement:
insert into external_account (id) values (?) [23505-196]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]

1 个解决方案

#1


0  

I solved the issue with these changes:

我用这些改变解决了这个问题:

  • Changing the mapping of the classes:
  • 更改类的映射:

Buyer class:

@Entity
public class Buyer extends User {

    @OneToOne(optional=true, cascade= {CascadeType.MERGE})
    @JoinColumn(nullable=true, unique=true)
    private ExternalAccount externalAccount;
    //getters and setters

}

Seller class:

@Entity
public class Seller extends User {

    @OneToOne(optional=false, cascade= {CascadeType.MERGE})
    @JoinColumn(nullable=false, unique=true)
    private ExternalAccount externalAccount;
    //getters and setters

}

ExternalAccount class:

@Entity
public class ExternalAccount {

    @Id
    private Long id;
    //getters and setters

}

And the most important part: Override Spring Data JPA save method to use entityManager.merge

而最重要的部分是:覆盖Spring Data JPA保存方法以使用entityManager.merge

@Service
public class BuyerService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public Buyer save(Buyer buyer) {
        return entityManager.merge(buyer);
    }

}

And the same thing with SellerService.

和SellerService一样。

#1


0  

I solved the issue with these changes:

我用这些改变解决了这个问题:

  • Changing the mapping of the classes:
  • 更改类的映射:

Buyer class:

@Entity
public class Buyer extends User {

    @OneToOne(optional=true, cascade= {CascadeType.MERGE})
    @JoinColumn(nullable=true, unique=true)
    private ExternalAccount externalAccount;
    //getters and setters

}

Seller class:

@Entity
public class Seller extends User {

    @OneToOne(optional=false, cascade= {CascadeType.MERGE})
    @JoinColumn(nullable=false, unique=true)
    private ExternalAccount externalAccount;
    //getters and setters

}

ExternalAccount class:

@Entity
public class ExternalAccount {

    @Id
    private Long id;
    //getters and setters

}

And the most important part: Override Spring Data JPA save method to use entityManager.merge

而最重要的部分是:覆盖Spring Data JPA保存方法以使用entityManager.merge

@Service
public class BuyerService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public Buyer save(Buyer buyer) {
        return entityManager.merge(buyer);
    }

}

And the same thing with SellerService.

和SellerService一样。