For better understanding, I want to achieve this:
为了更好地理解,我想实现这个目标:
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一样。