Hibernate/JPA中如何合併實體集合?
正確合併集合並不是一件容易的事!推薦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() + ")")); |
原始碼可以在這裡找到 。
相關文章
- 如何在Hibernate/JPA的實體和查詢中使用Java 8 Optional?Java
- JPA與hibernate-------JPA01
- Hibernate/JPA中@OneToOne和@MapsId的使用
- Hibernate/JPA中避免save()冗餘呼叫
- 如何透過Hibernate/JPA在MySQL中儲存UTC時區?MySql
- 如何在SpringBoot中使用Hibernate/JPA的@NaturalId?Spring Boot
- 影片合併分割軟體如何合併影片
- 如何使用Hibernate/JPA的JPQL/HQL查詢提取?
- 淺談JPA二:聊聊Hibernate
- 使用JPA和Hibernate延遲載入實體屬性的最佳方法 - Vlad Mihalcea
- hibernate及SpringBoot整合Jpa實現對資料庫操作Spring Boot資料庫
- 如何透過Hibernate/JPA的SqlResultSetMapping生成需要資料的DTO?SQLAPP
- Hibernate/JPA如何保證不生成多餘的SQL語句?SQL
- 【SpringBoot Demo】MySQL + JPA + Hibernate + Springboot + Maven DemoSpring BootMySqlMaven
- 如何在Hibernate/JPA中配置具有兩個連線池的兩個資料來源
- 使用Hibernate、JPA、Lombok遇到的有趣問題Lombok
- 影片合併分割軟體如何剪下影片
- pandas中如何使用合併append函式?APP函式
- python3.9中字典合併如何操作?Python
- hibernate在JPA規範中在控制檯無法出現SQL語句SQL
- 如何在VS Code中啟用實時合併衝突檢測?
- JPA和Hibernate的樂觀鎖與悲觀鎖
- Spring中如何配置Hibernate事務Spring
- Oracle資料庫與JPA和Hibernate 結合使用時的九個高效能技巧 - vladmihalceaOracle資料庫
- Java中的併發集合詳解Java
- hibernate不同實體不同填充建立人
- JPA實體中欄位對映補充和嵌入物件物件
- 在Go中如何實現併發Go
- Hibernate註解(一)之持久化實體持久化
- 學習Hibernate5 JPA這一篇就夠了
- SpringData JPA中儲存後重新整理並獲取實體Spring
- 不要在REST API中公開您的JPA實體 - Thorben JanssenRESTAPIORB
- ETL中雙流合併和多流合併的區別
- Java中如何使用泛型實現介面中的列表集合?Java泛型
- 如何合併視訊?是否適合新手操作?
- Hbase-原理-region合併和hfile的合併(大合併、小合併)
- 為啥國人偏愛Mybatis,而老外喜歡Hibernate/JPA呢?MyBatis
- 使用JPA和Hibernate呼叫儲存過程的最佳方法 - Vlad Mihalcea儲存過程