Hibernate/JPA中如何合併實體集合?

banq發表於2019-02-19

正確合併集合並不是一件容易的事!推薦Vlad的例子文章的Spring Boot示例,只有手工進行集合合併。
關鍵點:
  • 刪除傳入集合中不再存在的現有資料庫行。
  • 更新現有的可以在傳入集合中找到的資料庫行。
  • 新增在傳入集合中找到的行,這些行在當前資料庫快照中是找不到的。

假設tournament和tennis_player兩個表中有資料:

INSERT INTO tournament (id, name) VALUES (1, 'Roland Garros');
INSERT INTO tournament (id, name) VALUES (2, 'US Open');
INSERT INTO tennis_player (id, name, tournament_id) VALUES (1, 'Rafael Nadal', 1);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (2, 'Roger Federer', 1);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (3, 'David Ferer', 2);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (4, 'Andy Murray', 2);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (5, 'Del Potro', 2);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (6, 'Novak D', 2);
INSERT INTO tennis_player (id, name, tournament_id) VALUES (7, 'John Isner', 2);


Tournament實體:和TennisPlayer 是雙向一對多關係

@Entity
public class Tournament implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, 
            mappedBy = "tournament", orphanRemoval = true)
    private List<TennisPlayer> tennisPlayers = new ArrayList<>();


TennisPlayer 實體:

@Entity
public class TennisPlayer implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "tournament_id")
    private Tournament tournament;


倉儲:


@Repository
@Transactional(readOnly = true)
public interface TournamentRepository extends JpaRepository<Tournament, Long> { 
    
    @Query(value="SELECT t FROM Tournament t JOIN FETCH t.tennisPlayers WHERE t.name = ?1")
    Tournament tournamentAndPlayers(String name);
}

@Repository
@Transactional(readOnly = true)
public interface TennisPlayerRepository extends JpaRepository<TennisPlayer, Long> {

    @Query(value = "SELECT p FROM TennisPlayer p JOIN p.tournament t WHERE t.name = ?1")
    List<TennisPlayer> playersOfTournament(String name);
}


在服務中進行兩個實體集合的合併:


@Service
public class TennisService {

    private final TournamentRepository tournamentRepository;
    private final TennisPlayerRepository tennisPlayerRepository;

    public TennisService(TournamentRepository tournamentRepository,
            TennisPlayerRepository tennisPlayerRepository) {

        this.tournamentRepository = tournamentRepository;
        this.tennisPlayerRepository = tennisPlayerRepository;
    }

    public List<TennisPlayer> fetchPlayersOfTournament(String name) {

        return tennisPlayerRepository.playersOfTournament(name);
    }

    @Transactional
    public void updatePlayersOfTorunament(String name, List<TennisPlayer> players) {

        Tournament tournament = tournamentRepository.tournamentAndPlayers(name);
        System.out.println("-------------------------------------------------");

        // Remove the existing database rows that are no 
        // longer found in the incoming collection (players)
        //刪除傳入集合中不再存在的現有資料庫行
        tournament.getTennisPlayers().removeIf((t) -> !players.contains(t));

        // Update the existing database rows which can be found 
        // in the incoming collection (players)
        //更新現有的可以在傳入集合中找到的資料庫行

        List<TennisPlayer> newPlayers = players.stream()
                .filter((t) -> !tournament.getTennisPlayers().contains(t))
                .collect(Collectors.toList());

        players.stream()
                .filter((t) -> !newPlayers.contains(t))                
                .forEach((t) -> {
                    t.setTournament(tournament);
                    TennisPlayer mergedPlayer = tennisPlayerRepository.save(t);
                    tournament.getTennisPlayers().set(
                            tournament.getTennisPlayers().indexOf(mergedPlayer),
                            mergedPlayer);
                });

        // Add the rows found in the incoming collection, 
        // which cannot be found in the current database snapshot
        newPlayers.forEach((t) -> tournament.addTennisPlayer(t));
    }

}



手工合併集合的呼叫:

 System.out.println("------------------- Players from US Open --------------------");
            List<TennisPlayer> players = tennisService.fetchPlayersOfTournament("US Open");            
            
            players.forEach((t) -> System.out.println("Us Open: " + t.getName() + " | id:(" + t.getId() + ")"));
            
            System.out.println("---------- Players from US Open Updated Detached ------------");
            
            // ,update first player name
            players.get(0).setName("Fernando Verdasco");
            
            // remove second player
            players.remove(1);
                        
            // add a new player
            TennisPlayer player = new TennisPlayer();
            player.setName("Alexander Zverev");
            players.add(player);
            
            players.forEach((t) -> System.out.println("Us Open: " + t.getName() + " | id:(" + t.getId() + ")"));
            
            System.out.println("----------------- Players from US Open Merged ----------------");
            tennisService.updatePlayersOfTorunament("Us Open", players);
            
            players.forEach((t) -> System.out.println("Us Open: " + t.getName() + " | id:(" + t.getId() + ")"));




原始碼可以在這裡找到  。

相關文章