如何更新@OneToMany Hibernate 关系
How to update @OneToMany Hibernate relationship
我无法更新依赖对象列表。我有一个 API 应该更新客户帐户列表。
一个客户 - 许多帐户。
我按照指示配置了@OneToMany 以进行正确更新:
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
实体:
@Entity
@Getter
@Setter
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id_client")
private Integer id;
private String name;
private int age;
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Account> accounts = new ArrayList<>();
}
@Entity
@Getter
@Setter
public class Account {
@Id
@GeneratedValue
@Column(name = "id_account")
private Integer id;
private int amount;
private String currency;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_client")
private Client client;
}
我想对所提供的数据执行的操作。我已经有一个客户 (id = 100) 有两个帐户 (id = 10, 11)。
我想进行更新,以便客户端具有不同的帐户列表 id:10、12。
我的测试和测试数据。
<dataset>
<Client id_client="100" name="John" age="23"/>
<Client id_client="101" name="Mike" age="28"/>
<Client id_client="102" name="Kevin" age="19"/>
<Account id_account="10" amount="50" currency="USD" id_client="100"/>
<Account id_account="11" amount="100" currency="USD" id_client="100"/>
<Account id_account="12" amount="150" currency="EUR" id_client="101"/>
<Account id_account="13" amount="200" currency="EUR" id_client="102"/>
</dataset>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@TestExecutionListeners({
TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
@Transactional
@DatabaseSetup("/data.xml")
public class HibTest {
@PersistenceContext
protected EntityManager em;
protected Session session;
@Before
public void dbAllSet() {
session = em.unwrap(Session.class);
}
@Test
@Commit
public void mergeCollections() {
Client client = session.get(Client.class, 100); // with accounts: 10, 11
List<Account> newUpdatedListAccount = newUpdatedListAccount();
client.getAccounts().clear();
client.getAccounts().addAll(newUpdatedListAccount);
session.saveOrUpdate(client);
session.flush();
Account account12 = session.get(Account.class, 12);
System.out.println(account12.getClient().getId()); // 101 nothing has changed, must be 100
}
private List<Account> newUpdatedListAccount() {
ArrayList<Account> accounts = new ArrayList<>();
accounts.add(session.get(Account.class, 12)); // new account from other client
accounts.add(session.get(Account.class, 10)); // existing account in updated client
return accounts;
}
}
但是更新不起作用。并且更新不会显示在 sql 日志中。
如何正确更新?这种情况很常见。
您必须在帐户中设置 'client' 或更好 - 使用 addAccount
方法:
您的客户class
@Entity @Getter @Setter
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id_client")
private Integer id;
private String name;
private int age;
// Here you say that hibernate shall use the field `client` in account for mapping !!!
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Account> accounts = new ArrayList<>();
// This make sure that our bidi-relation works well
public void addAccount(Account account){
accounts.add(account);
if( account.getClient() != this ) {
account.setClient(this);
}
}
// This is convenient method
public void addAccounts(Collection<Account> accounts){
for( Account account : accounts ){
this.addAccount(account);
}
}
// And if you remove an account you have to remove the `client` from the account
public void removeAccount(int id){
for( Account account : accounts ){
if( Objects.equals(account.getId(), id) ){
accounts.remove(account);
account.setClient(null);
break;
}
}
}
void clearAccounts() {
for( Account account : accounts ){
account.setClient(null);
}
accounts.clear();
}
// We lose control if anybody can set it's own list.
public void setAccounts(List<Account> accounts){
throw new UnsupportedOperationException("Do not use this");
}
// Same here - We lose control if anybody can change our List
public List<Account> getAccounts (){
return Collections.unmodifiableList(accounts);
}
}
您的帐户Class
@Entity @Getter @Setter
@Table(name = "accounts")
public class Account {
@Id
@GeneratedValue
@Column(name = "id_account")
private Integer id;
private int amount;
private String currency;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_client")
private Client client;
// This make sure that our bidi-relation works well
public void setClient(Client client){
this.client = client;
if( client != null && ! client.getAccounts().contains(this) ){
client.addAccount(this);
}
}
}
你的测试
public class HibTest {
@PersistenceContext
protected EntityManager em;
protected Session session;
@Before
public void dbAllSet() {
session = em.unwrap(Session.class);
}
@Test
@Commit
public void mergeCollections() {
Client client = (Client) session.get(Client.class, 100); // with accounts: 10, 11
List<Account> newUpdatedListAccount = newUpdatedListAccount();
/* YOUR CODE
*
* You tell hibernate to clear the list with the account. If you save your changes nothing will happen because
* you have told hibernate that the relation is the `client` field in the Account class (mappedby="client") and
* we didn't change the `client` field in the Account class.
*/
// client.getAccounts().clear();
/*
* Same here - you add accounts with a reference to Client with ID 101 and we did not change it.
*/
// client.getAccounts().addAll(newUpdatedListAccount);
// do not use the client.getAccounts() list directly
client.clearAccounts();
client.addAccounts(newUpdatedListAccount);
session.saveOrUpdate(client);
session.flush();
Account account12 = (Account) session.get(Account.class, 12);
System.out.println(account12.getClient().getId()); // 101 nothing has changed, must be 100
}
private List<Account> newUpdatedListAccount() {
ArrayList<Account> accounts = new ArrayList<>();
accounts.add((Account) session.get(Account.class, 12)); // new account from other client
accounts.add((Account) session.get(Account.class, 10)); // existing account in updated client
return accounts;
}
}
我无法更新依赖对象列表。我有一个 API 应该更新客户帐户列表。 一个客户 - 许多帐户。
我按照指示配置了@OneToMany 以进行正确更新:
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
实体:
@Entity
@Getter
@Setter
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id_client")
private Integer id;
private String name;
private int age;
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Account> accounts = new ArrayList<>();
}
@Entity
@Getter
@Setter
public class Account {
@Id
@GeneratedValue
@Column(name = "id_account")
private Integer id;
private int amount;
private String currency;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_client")
private Client client;
}
我想对所提供的数据执行的操作。我已经有一个客户 (id = 100) 有两个帐户 (id = 10, 11)。
我想进行更新,以便客户端具有不同的帐户列表 id:10、12。
我的测试和测试数据。
<dataset>
<Client id_client="100" name="John" age="23"/>
<Client id_client="101" name="Mike" age="28"/>
<Client id_client="102" name="Kevin" age="19"/>
<Account id_account="10" amount="50" currency="USD" id_client="100"/>
<Account id_account="11" amount="100" currency="USD" id_client="100"/>
<Account id_account="12" amount="150" currency="EUR" id_client="101"/>
<Account id_account="13" amount="200" currency="EUR" id_client="102"/>
</dataset>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@TestExecutionListeners({
TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
@Transactional
@DatabaseSetup("/data.xml")
public class HibTest {
@PersistenceContext
protected EntityManager em;
protected Session session;
@Before
public void dbAllSet() {
session = em.unwrap(Session.class);
}
@Test
@Commit
public void mergeCollections() {
Client client = session.get(Client.class, 100); // with accounts: 10, 11
List<Account> newUpdatedListAccount = newUpdatedListAccount();
client.getAccounts().clear();
client.getAccounts().addAll(newUpdatedListAccount);
session.saveOrUpdate(client);
session.flush();
Account account12 = session.get(Account.class, 12);
System.out.println(account12.getClient().getId()); // 101 nothing has changed, must be 100
}
private List<Account> newUpdatedListAccount() {
ArrayList<Account> accounts = new ArrayList<>();
accounts.add(session.get(Account.class, 12)); // new account from other client
accounts.add(session.get(Account.class, 10)); // existing account in updated client
return accounts;
}
}
但是更新不起作用。并且更新不会显示在 sql 日志中。 如何正确更新?这种情况很常见。
您必须在帐户中设置 'client' 或更好 - 使用 addAccount
方法:
您的客户class
@Entity @Getter @Setter
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id_client")
private Integer id;
private String name;
private int age;
// Here you say that hibernate shall use the field `client` in account for mapping !!!
@OneToMany(mappedBy = "client", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Account> accounts = new ArrayList<>();
// This make sure that our bidi-relation works well
public void addAccount(Account account){
accounts.add(account);
if( account.getClient() != this ) {
account.setClient(this);
}
}
// This is convenient method
public void addAccounts(Collection<Account> accounts){
for( Account account : accounts ){
this.addAccount(account);
}
}
// And if you remove an account you have to remove the `client` from the account
public void removeAccount(int id){
for( Account account : accounts ){
if( Objects.equals(account.getId(), id) ){
accounts.remove(account);
account.setClient(null);
break;
}
}
}
void clearAccounts() {
for( Account account : accounts ){
account.setClient(null);
}
accounts.clear();
}
// We lose control if anybody can set it's own list.
public void setAccounts(List<Account> accounts){
throw new UnsupportedOperationException("Do not use this");
}
// Same here - We lose control if anybody can change our List
public List<Account> getAccounts (){
return Collections.unmodifiableList(accounts);
}
}
您的帐户Class
@Entity @Getter @Setter
@Table(name = "accounts")
public class Account {
@Id
@GeneratedValue
@Column(name = "id_account")
private Integer id;
private int amount;
private String currency;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_client")
private Client client;
// This make sure that our bidi-relation works well
public void setClient(Client client){
this.client = client;
if( client != null && ! client.getAccounts().contains(this) ){
client.addAccount(this);
}
}
}
你的测试
public class HibTest {
@PersistenceContext
protected EntityManager em;
protected Session session;
@Before
public void dbAllSet() {
session = em.unwrap(Session.class);
}
@Test
@Commit
public void mergeCollections() {
Client client = (Client) session.get(Client.class, 100); // with accounts: 10, 11
List<Account> newUpdatedListAccount = newUpdatedListAccount();
/* YOUR CODE
*
* You tell hibernate to clear the list with the account. If you save your changes nothing will happen because
* you have told hibernate that the relation is the `client` field in the Account class (mappedby="client") and
* we didn't change the `client` field in the Account class.
*/
// client.getAccounts().clear();
/*
* Same here - you add accounts with a reference to Client with ID 101 and we did not change it.
*/
// client.getAccounts().addAll(newUpdatedListAccount);
// do not use the client.getAccounts() list directly
client.clearAccounts();
client.addAccounts(newUpdatedListAccount);
session.saveOrUpdate(client);
session.flush();
Account account12 = (Account) session.get(Account.class, 12);
System.out.println(account12.getClient().getId()); // 101 nothing has changed, must be 100
}
private List<Account> newUpdatedListAccount() {
ArrayList<Account> accounts = new ArrayList<>();
accounts.add((Account) session.get(Account.class, 12)); // new account from other client
accounts.add((Account) session.get(Account.class, 10)); // existing account in updated client
return accounts;
}
}