Как обновить отношения @OneToMany Hibernate - PullRequest
0 голосов
/ 05 февраля 2019

Не могу обновить список зависимых объектов.У меня есть 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).

Я хочу обновить, чтобы у клиента был другой список учетных записей с идентификатором: 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.Как правильно обновить?Это очень частый случай.

1 Ответ

0 голосов
/ 06 февраля 2019

Вы должны установить «клиент» в учетной записи или намного лучше - используйте метод addAccount:

Ваш класс клиента

@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);
    }

}

Ваш класс аккаунта

@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;
    }
}
...