实体Player:玩家。
实体Game:游戏。
玩家和游戏是多对多的关系。一个玩家可以玩很多的游戏,一个游戏也可以被很多玩家玩。
JPA中使用@ManyToMany来注解多对多的关系,由一个关联表来维护。这个关联表的表名默认是:主表名+下划线+从表名。(主表是指关系维护端对应的表,从表指关系被维护端对应的表)。这个关联表只有两个外键字段,分别指向主表ID和从表ID。字段的名称默认为:主表名+下划线+主表中的主键列名,从表名+下划线+从表中的主键列名。
需要注意的:
1、多对多关系中一般不设置级联保存、级联删除、级联更新等操作。
2、可以随意指定一方为关系维护端,在这个例子中,我指定Player为关系维护端,所以生成的关联表名称为: player_game,关联表的字段为:player_id和game_id。
3、多对多关系的绑定由关系维护端来完成,即由Player.setGames(games)来绑定多对多的关系。关系被维护端不能绑定关系,即Game不能绑定关系。
4、多对多关系的解除由关系维护端来完成,即由Player.getGames().remove(game)来解除多对多的关系。关系被维护端不能解除关系,即Game不能解除关系。
5、如果Player和Game已经绑定了多对多的关系,那么不能直接删除Game,需要由Player解除关系后,才能删除Game。但是可以直接删除Player,因为Player是关系维护端,删除Player时,会先解除Player和Game的关系,再删除Player。
Player.java如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | package com.cndatacom.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; /** * 玩家 * @author Luxh */ @Entity @Table (name= "player" ) public class Player { @Id @GeneratedValue private Long id; /**玩家姓名*/ @Column (length= 32 ) private String name; /**玩家玩的游戏*/ @ManyToMany @JoinTable (name= "player_game" ,joinColumns= @JoinColumn (name= "player_id" ), inverseJoinColumns= @JoinColumn (name= "game_id" )) //关系维护端,负责多对多关系的绑定和解除 //@JoinTable注解的name属性指定关联表的名字,joinColumns指定外键的名字,关联到关系维护端(Player) //inverseJoinColumns指定外键的名字,要关联的关系被维护端(Game) //其实可以不使用@JoinTable注解,默认生成的关联表名称为主表表名+下划线+从表表名, //即表名为player_game //关联到主表的外键名:主表名+下划线+主表中的主键列名,即player_id //关联到从表的外键名:主表中用于关联的属性名+下划线+从表的主键列名,即game_id //主表就是关系维护端对应的表,从表就是关系被维护端对应的表 private Set<Game> games = new HashSet<Game>(); public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Set<Game> getGames() { return games; } public void setGames(Set<Game> games) { this .games = games; } } |
Game.java如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package com.cndatacom.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; /** * 游戏 * @author Luxh */ @Entity @Table (name= "game" ) public class Game { @Id @GeneratedValue private Long id; /**游戏名称*/ @Column (length= 32 ) private String name; /**游戏拥有的玩家*/ @ManyToMany (mappedBy= "games" ) //只需要设置mappedBy="games"表明Game实体是关系被维护端就可以了 //级联保存、级联删除等之类的属性在多对多关系中是不需要设置 //不能说删了游戏,把玩家也删掉,玩家还可以玩其他的游戏 private Set<Player> players = new HashSet<Player>(); public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Set<Player> getPlayers() { return players; } public void setPlayers(Set<Player> players) { this .players = players; } } |
简单的测试如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | package com.cndatacom.jpa.test; import java.util.HashSet; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.cndatacom.jpa.entity.Game; import com.cndatacom.jpa.entity.Player; public class TestManyToMany { EntityManagerFactory emf = null ; @Before public void before() { //根据在persistence.xml中配置的persistence-unit name 创建EntityManagerFactory emf = Persistence.createEntityManagerFactory( "myJPA" ); } @After public void after() { //关闭EntityManagerFactory if ( null != emf) { emf.close(); } } /** * 创建玩家和游戏 */ @Test public void testSavePlayerAndGame() { EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); //因为Player和Game之间没有级联保存的关系,所以Playe和Game要分别保存 Player player = new Player(); player.setName( "西门吹雪" ); //保存Player em.persist(player); Game game = new Game(); game.setName( "大菠萝3" ); //保存game em.persist(game); em.getTransaction().commit(); em.close(); } /** * 给玩家添加游戏 */ @Test public void testAddGameToPlayer() { EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); //找出ID为1的玩家 Player player = em.find(Player. class , 1L); //找出ID为1的游戏 Game game = em.find(Game. class , 1L); Set<Game> games = new HashSet<Game>(); games.add(game); //因为Player是关系的维护端,所以必须由Player来添加关系 player.setGames(games); em.getTransaction().commit(); em.close(); } /** * 玩家删除游戏 */ @Test public void testRemoveGameFormPlayer() { EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); //找出ID为1的玩家 Player player = em.find(Player. class , 1L); //找出ID为1的游戏 Game game = em.find(Game. class , 1L); //因为Player是关系维护端,所以关系的解除由Player来完成 player.getGames().remove(game); em.getTransaction().commit(); em.close(); } } |
生成的关联表结构如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!