2017-08-08 21 views
0

爲了更好的理解,我想實現這一點:@OneToOne到同一類

External Account example

注:買家可能沒有ExternalAccount但賣家必須擁有它。我有什麼/試過:

採購類別:

@Entity 
public class Buyer extends User { 

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

} 

賣家等級:

@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類:

@Entity 
public class ExternalAccount { 

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

} 

我使用Spring數據JPA與Spring引導和我想要的是:

  • 如果沒有買方相關但存在ExternalAccount(與賣方相關聯),則將其關聯。

  • 如果沒有賣方相關但ExternalAccount存在(與買方相關聯),則將其關聯。

  • 如果沒有ExternalAccount存在,當保存Buyer/Seller時,創建ExternalAccount。

,但使用這種不尊重@OneToOne映射(閱讀了大量的#2帖子後),我可以實現與CascadeType.MERGE類似的行爲。它允許創建許多與相同的ExternalAccount相關的買家。

  • 我用數據庫測試創建了一個github項目來重現問題。

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

,我有我的例子休息控制器(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("[email protected]"); 
    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("[email protected]"); 
    seller.setName("Seller Name"); 
    ExternalAccount external = new ExternalAccount(); 
    external.setId(123L); 
    seller.setExternalAccount(external); 
    sellerDao.save(seller); 
    return seller; 
} 

當我在第一時間打電話/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] 
+0

如果將externalAccount關係移動到User類,該怎麼辦? (因爲買方可能沒有賬戶,所以它應該是可空的) – toongeorges

+0

然後在理論上我可以讓賣方沒有ExternalAccount ...這可能是我的出路,但我想要更接近這種方法。它的語義是正確的。 –

回答

0

我解決了這些變化的問題:

  • 更改類的映射:

買家等級:

@Entity 
public class Buyer extends User { 

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

} 

賣家類:

@Entity 
public class Seller extends User { 

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

} 

ExternalAccount類:

@Entity 
public class ExternalAccount { 

    @Id 
    private Long id; 
    //getters and setters 

} 

而且最重要的部分:覆蓋彈簧數據JPA保存方法使用entityManager.merge

@Service 
public class BuyerService { 

    @PersistenceContext 
    private EntityManager entityManager; 

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

} 

和SellerService一樣。