精华内容
下载资源
问答
  • ManyToMany
    千次阅读
    2019-04-03 10:48:08

    1.Many-to-Many映射(多对多映射关系)

    多对多通常是通过建立一张中间表,然后由任一一个多的一方来维护关联关系.

    2.一方:

    @ManyToMany(mappedBy="teachers")  --->  表示由另一方来进行维护

    3另一方:

    @ManyToMany   ---> ManyToMany指定多对多的关联关系

    @JoinTable(name="t_teacher_course",joinColumns={ @JoinColumn(name="stuid")}, inverseJoinColumns={ @JoinColumn(name = "tid") })  --->  因为多对多之间会通过一张中间表来维护两表直接的关系,所以通过 JoinTable 这个注解来声明,name就是指定了中间表的名字JoinColumns是一个 @JoinColumn类型的数组,表示的是我这方在对方中的外键名称,inverseJoinColumns也是一个 @JoinColumn类型的数组,表示的是对方在我这放中的外键名称.

    (1)这是hibernate.cfg.xml中的内容

    <!DOCTYPE hibernate-configuration PUBLIC
    	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
    	<session-factory>
    	    <!-- 显示sql语句 -->
    		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>   <!-- 启动 -->
    		<property name="show_sql">true</property>
    		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><!-- 获得链接-->
    		 <!--test是数据库的名字       characterEncoding=utf-8是语言    -->
    		<property name="hibernate.connection.url">jdbc:mysql:///test?characterEncoding=utf-8</property>
    		<property name="hibernate.connection.username">root</property>  <!-- 用户名 -->
    		<property name="hibernate.connection.password"></property> <!-- 密码 -->
    		<!--实体类配置文件的路径  -->
    		<mapping resource="pojo/Person.hbm.xml"/>
    		
    	</session-factory>
    </hibernate-configuration>

    (2)创建pojo对象,并在同一个包中创建对应的配置文件,命名为pojo名称

    package com.oracle.pojo;
    
    import java.util.List;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="student")
    public class Student {
    	@Id
    	private int stuId;
    	private String stuName;
    	@ManyToMany
    	@JoinTable(name="student_teacher",joinColumns={@JoinColumn(name="tid")},inverseJoinColumns={@JoinColumn(name="stuid")})
    	
    	private  List<Teacher>	teachers;
    	public Student() {
    		super();
    		// TODO Auto-generated constructor stub
    	}
    	public Student(int stuId, String stuName) {
    		super();
    		this.stuId = stuId;
    		this.stuName = stuName;
    		
    	}
    	public int getStuId() {
    		return stuId;
    	}
    	public void setStuId(int stuId) {
    		this.stuId = stuId;
    	}
    	public String getStuName() {
    		return stuName;
    	}
    	public void setStuName(String stuName) {
    		this.stuName = stuName;
    	}
    	public List<Teacher> getTeachers() {
    		return teachers;
    	}
    	public void setTeachers(List<Teacher> teachers) {
    		this.teachers = teachers;
    	}
    	@Override
    	public String toString() {
    		return "Student [stuId=" + stuId + ", stuName=" + stuName +  "]";
    	}
    	
    
    }
    
    package com.oracle.pojo;
    
    import java.util.List;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="teacher")
    public class Teacher {
    	@Id
    	private int tId;
    	private String tName;
    	@ManyToMany(mappedBy="teachers")
    	private  List<Student>	students;
    	public int gettId() {
    		return tId;
    	}
    	public void settId(int tId) {
    		this.tId = tId;
    	}
    	public String gettName() {
    		return tName;
    	}
    	public void settName(String tName) {
    		this.tName = tName;
    	}
    	public List<Student> getStudents() {
    		return students;
    	}
    	public void setStudents(List<Student> students) {
    		this.students = students;
    	}
    	public Teacher(int tId, String tName) {
    		super();
    		this.tId = tId;
    		this.tName = tName;
    		
    	}
    	public Teacher() {
    		super();
    		// TODO Auto-generated constructor stub
    	}
    	@Override
    	public String toString() {
    		return "Teacher [tId=" + tId + ", tName=" + tName + ", students=" + students + "]";
    	}
    	
    }
    

    增加

    package com.oracle.teat;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.oracle.pojo.Student;
    import com.oracle.pojo.Teacher;
    import com.oracle.until.Until;
    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Session session=Until.getSession();
    		Transaction transaction=session.beginTransaction();
    		Teacher teacher  = new Teacher(101, "王老师");
    		Teacher teacher2  = new Teacher(102, "数学老师");
    		Student student1= new  Student(2001, "张三");
    		Student student2= new  Student(2002, "李四");
    		Student student3= new  Student(2003, "王力宏");
    		Student student4= new  Student(2004, "赵四");
    		Student student5= new  Student(2005, "刘能");
    		Student student6= new  Student(2006, "谢广坤");
    		List<Teacher> teachers = new ArrayList<>();
    		teachers.add(teacher);
    		teachers.add(teacher2);
    		student1.setTeachers(teachers);
    		student2.setTeachers(teachers);
    		student3.setTeachers(teachers);
    		student4.setTeachers(teachers);
    		student5.setTeachers(teachers);
    		student6.setTeachers(teachers);
    			
    		//保存老师
    		session.save(teacher);
    		session.save(teacher2);
    		//保存学生
    		session.save(student1);
    		session.save(student2);
    		session.save(student3);
    		session.save(student4);
    		session.save(student5);
    		session.save(student6);
    	
    
    		transaction.commit();
    		Until.closeSession();
    		
    	}
    
    }
    

    查询

    package com.oracle.teat;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.oracle.pojo.Student;
    import com.oracle.pojo.Teacher;
    import com.oracle.until.Until;
    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Session session=Until.getSession();
    		Transaction transaction=session.beginTransaction();
    		
    	
    	Teacher teacher=	session.load(Teacher.class, 101);
    		System.out.println(teacher);
    		transaction.commit();
    		Until.closeSession();
    		
    	}
    
    }
    

     

    更多相关内容
  • 主要介绍了spring jpa ManyToMany原理及用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了Django多层嵌套ManyToMany字段ORM操作详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 多对多用于多态和非多态ManyToMany关系的Laravel Nova字段。目录特征在创建和更新页面中附加多态和非多态的ManyToMany关系附加关系时编辑枢轴列将源多次附加到另一个资源安装composer require armincms/many-to-many...
  • hibernate5_ManyToMany多对多单向维护 hibernate5_ManyToMany多对多单向维护
  • 使用jQuery UI自动完成功能,在Django Admin中编辑ForeignKey , ManyToManyField和CharField 。文献资料安装pip install django-ajax-selects 添加应用: # settings.pyINSTALLED_APPS = ( ......
  • ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。 以下是本篇笔记的目录: ManyToMany 的介绍 through 参数 through_fields 参数 ManyToMany关系数据的增删改查 OneToOne介绍 1、...

    ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。

    以下是本篇笔记的目录:

    1. ManyToMany 的介绍
    2. through 参数
    3. through_fields 参数
    4. ManyToMany关系数据的增删改查
    5. OneToOne介绍

    1、ManyToMany 的介绍

    假设有两个 model,Person 和 Group,这两个model之间是多对多的关系。那么我们可以如下创建其关系:

    # blog/models.py
    
    class Person(models.Model):
    	name = models.CharField(max_length=64)
    
    class Group(models.Model):
    	name = models.CharField(max_length=64)
    	members = models.ManyToManyField(Person)
    

    通过上述代码我们就创建了有多对多关系两个model,当我们执行 migrate 操作之后(可以先不执行,后续还会对其有所更改),系统除了会创建 Person 和 Group 这两个表之外,还会创建一个表。

    表名为 blog_group_members,因为是放在 Blog 这个 application 下面,所以,表名的前缀是 blog,然后加上 model 名的小写为 group,加上字段名称 members。

    这张第三方表会有两个字段,person_id 和 group_id,将这两个 model 关联起来。

    通过往这张第三方表写入 person_id 和 group_id的数据,我们就将这两个 model 关联了起来。

    而获取他们对应的关系的记录和 ForeignKey 的关系类似:

    根据 Person 数据查询关联的 Group 数据:

    person = Person.objects.get(id=1)
    group_list = person.group_set.all()  # 使用 Group 的小写加 _set
    

    根据 Group 数据查询关联的 Person 数据,这个查询方法略微不同,用到的是 Group 定义的 members 字段:

    group = Group.objects.get(id=1)
    person = group.members.all() 
    
    # 根据条件来搜索 person 也是可以的
    person = group.members.filter(name='hunter')
    

    2、through参数

    上面 ManyToMany 的定义中,我们没有加任何参数,所以自动创建的表名是默认的,字段也只是两个 model 的主键id。

    而如果我们有一些额外的需求,比如说,为 Person 和 Group 添加关联关系时,需要加上关联时间,或者想自己指定表名或 model 名的时候,我们可以通过 through 属性来指定 model 的名称,然后为其添加我们需要的字段。

    比如我们想为 Person 和 Group 创建一个多对多的关系,指定model 名为 Membership,且额外添加字段,比如添加时间,可以通过 through 参数来指定:

    class Person(models.Model):
        name = models.CharField(max_length=50)
    
    class Group(models.Model):
        name = models.CharField(max_length=128)
        members = models.ManyToManyField(
            Person,
            through='Membership',
        )
    
    class Membership(models.Model):
        person = models.ForeignKey(Person, on_delete=models.CASCADE)
        group = models.ForeignKey(Group, on_delete=models.CASCADE)
        date_joined = models.DateField()
        
    

    3、through_fileds参数

    在我们上面创建的 Membership model 中,我们对应的多对多的字段分别是 person 和 group,所以系统可以自动找到对应的多对多的字段。

    如果在第三方表,也就是 Membership 中,有多个相同的 Person 或者 Group 的字段,就需要通过 through_fields 参数来指定多对多的字段:

    class Person(models.Model):
        name = models.CharField(max_length=50)
    
    
    class Group(models.Model):
        name = models.CharField(max_length=128)
        members = models.ManyToManyField(
            Person,
            through='Membership',
            through_fields=('group', 'person'),
        )
    
    
    class Membership(models.Model):
        group = models.ForeignKey(Group, on_delete=models.CASCADE)
        person = models.ForeignKey(Person, on_delete=models.CASCADE)
        inviter = models.ForeignKey(
            Person,
            on_delete=models.CASCADE,
            related_name="membership_invites",
        )
        invite_reason = models.CharField(max_length=64)
    

    4、ManyToMany关系数据的增删改查

    接下来,我们定下最终的几个 model 内容如下,用于演示 ManyToMany 的增删改查的操作:

    class Person(models.Model):
        name = models.CharField(max_length=50)
    
    class Group(models.Model):
        name = models.CharField(max_length=128)
        members = models.ManyToManyField(
            Person,
            through='Membership',
        )
    
    class Membership(models.Model):
        person = models.ForeignKey(Person, on_delete=models.CASCADE)
        group = models.ForeignKey(Group, on_delete=models.CASCADE)
        date_joined = models.DateField()
        invite_reason = models.CharField(max_length=64)
        
    

    现在我们有 Person 和 Group 两个model,还有两个 model 之间的关系表 Membership,如果我们要创建一个对应的关系,则需要创建一个 Membership 实例。

    创建

    首先创建 Person 和 Group 的记录:

    from blog.models import Person, Group, Membership
    hunter = Person.objects.create(name='hunter')
    group_1 = Group.objects.create(name='group_1')
    

    创建多对多记录:

    m1 = Membership.objects.create(person=hunter, group=group_1, date_joined='2022-01-01', invite_reason='xxx')
    

    根据单个 Person 记录获取所有相关 Group 记录,使用方法同外键搜索方法:

    groups = hunter.group_set.all()
    

    根据单个 Group 记录获取所有相关 Person 记录,根据多对多字段来搜索:

    persons = group_1.members.all()
    

    根据 Membership 关系记录获取 Person 和 Group 记录,可以直接用外键的的方式使用:

    m1.person
    m1.group
    

    根据 Group 使用 add 添加一条多对多记录:

    paul = Person.objects.create(name='pual')
    group_1.members.add(paul, through_defaults={'date_joined': '2022-01-01'})
    

    其中,through_defaults 参数为字典,内容为多对多关系表的额外添加的字段。

    根据 Group 使用 create 创建一条多对多记录:

    如果没有相应的 Person 记录,可以根据 Group 来直接创建

    group_1.members.create(name='mary', through_defaults={'date_joined': '2022-01-01'})
    

    根据 Group 使用 set 刷新多对多记录:

    使用 set 方法来设置多对多的关系:

    jack = Person.objects.create(name='jack')
    lucy = Person.objects.create(name='lucy')
    group_1.members.set([jack, lucy], through_defaults={'date_joined': '2022-01-01'})
    

    需要注意的是,使用 set() 方法加上关联之后,这个 Group 实例之前设置的关联数据都会被清除。

    也就是说,set() 里设置的关联数据就是最终所有的关联数据。

    根据 Group 使用 remove 删除一条多对多记录:

    在上面 我们使用了 set() 方法设置了两条关联数据,jack 和 lucy,现在我们想要把 jack——group_1 这条关系删除,可使用 remove() 方法:

    group_1.members.remove(jack)
    

    使用 clear 清除某个 Group 实例上所有关系:

    group_1.members.clear()
    

    多对多的搜索:

    根据 Person 的条件搜索 Group 的数据:

    比如搜索 Person 的 name 字段为 ‘hunter’ 的 Group 数据

    Group.objects.filter(members__name='hunter')
    

    根据 Group 的条件搜索 Person 的数据:

    比如搜索 Group 的 name 字段为 ‘group_1’ 的Person关联数据:

    Person.objects.filter(group__name='group_1')
    

    如果要搜索额外的关联字段:

    Person.objects.filter(group__name='group_1', membership__date_joined='2022-01-01')
    

    5、OneToOne 介绍

    不同于 多对一和多对多的关系,OneToOne 是一对一的关系,也就是说 一条数据仅能被另一条数据关联。

    下面是两个 OneToOne 对应的 model:

    class Place(models.Model):
        name = models.CharField(max_length=50)
        address = models.CharField(max_length=80)
    
        def __str__(self):
            return "%s the place" % self.name
    
    
    class Restaurant(models.Model):
        place = models.OneToOneField(Place, on_delete=models.CASCADE, default=None, related_name='place_restaurant', null=True)
    

    接下来创建两条数据:

    r_1 = Restaurant.objects.create()
    p_1 = Place.objects.create(name='beijing', address='beijing')
    

    根据 Restaurant 获取 Place 数据,直接根据字段获取数据:

    r_1.place
    

    如果根据 Place 获取 Restaurant 数据,因为是 OneToOne 的关系,所以可以直接获取:

    上面的 model 中我们定义了 related_name,所以是:

    p_1.place_restaurant
    

    如果没有定义 related_name 和 related_query_name 那么就是 model 的小写:

    p_1.restaurant
    

    但是从 Place 到 Restaurant 获取数据,如果没有这种 OneToOne 的对应,比如我们上面直接创建的 p_1,使用这种方式获取关联数据就会报错,因为没有这种 OneToOne 的数据。

    那么这个时候我们可以先判断是否有对应的这种 OneToOne 的数据:

    hasattr(p_1, 'place_restaurant')  # 返回的是布尔型数据 
    
    # 或者 
    getattr(p_1, 'place_restaurant', None)
    

    以上就是这篇笔记全部内容,接下来将要介绍 model 里的 Meta 的用法。

    本文首发于本人微信公众号:Django笔记。
    如果想获取更多相关文章,可扫码关注阅读:

    在这里插入图片描述

    展开全文
  • 最近被ManyToMany注解死循环及一些复杂SQL的操作搞得焦头烂额,这里给出这几天对这方面概念的思考。 相信这篇文章不会浪费朋友们的时间,也希望朋友们能中得到收获。demo中没有严格区别VO,PO,DTO,DO等概念,请...

    由于项目中用了Hibernate和JPA。最近被ManyToMany注解死循环及一些复杂SQL的操作搞得焦头烂额,这里给出这几天对这方面概念的思考。

    相信这篇文章不会浪费朋友们的时间,也希望朋友们能中得到收获。demo中没有严格区别VO,PO,DTO,DO等概念,请朋友们不要在意这些细节。

    闲话少说,直接上全部代码,文章后面有具体的分析。

    代码部分

    项目目录

    数据库表概述

    学生表:存储任务信息

    教师表:存储任务详情信息

    关系

    任务详情 N:N 标签(外键关联)

    建表语句

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    DROP TABLE IF EXISTS `students`;
    DROP TABLE IF EXISTS `teachers`;
    DROP TABLE IF EXISTS `teachers_students`;
    
    CREATE TABLE `students`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
      `gender` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '学生表' ROW_FORMAT = Dynamic;
    
    CREATE TABLE `teachers`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `tname` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '教师表' ROW_FORMAT = Dynamic;
    
    CREATE TABLE `teachers_students`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `sid` int(11) NULL DEFAULT NULL,
      `tid` int(11) NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
    
    alter table teachers_students add constraint FK_Reference_1 foreign key (sid)
          references students (id) on delete restrict on update restrict;
    
    alter table teachers_students add constraint FK_Reference_2 foreign key (tid)
          references teachers (id) on delete restrict on update restrict;
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    maven依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.5</version>
            <relativePath/>
        </parent>
    
        <groupId>com.yipeng.task</groupId>
        <artifactId>task-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <!--统一管理jar包和版本-->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <junit.version>4.12</junit.version>
            <log4j.version>1.2.17</log4j.version>
            <lombok.version>1.16.18</lombok.version>
            <mysql.version>8.0.18</mysql.version>
            <druid.verison>1.1.16</druid.verison>
        </properties>
    
        <dependencies>
            <!--spring boot 2.4.5-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.76</version>
            </dependency>
    
    
            <!-- MySql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- Druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.verison}</version>
            </dependency>
    
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!-- log4j -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <fork>true</fork>
                        <addResources>true</addResources>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>

    yaml文件

    server:
      port: 8888
    
    spring:
      application:
        name: demo
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT&characterEncoding=UTF-8&useSSL=true
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        initialSize: 5
        minIdle: 5
        maxActive: 10
        testWhileIdle: true
        maxWait: 1000
        validationQuery:  SELECT 1

    DataSourceConfig (最简单的配置)

    package com.yipeng.task.demo.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:43
     */
    @Slf4j
    @Configuration
    public class DataSourceConfig {
    
        @Value("${spring.datasource.url}")
        private String url;
    
        @Value("${spring.datasource.driver-class-name}")
        private String driverClassName;
    
        @Value("${spring.datasource.username}")
        private String userName;
    
        @Value("${spring.datasource.password}")
        private String password;
    
        @Bean
        public DataSource dataSource() {
    
            log.info("url: " + url);
            log.info("driverClassName: " + driverClassName);
            log.info("userName: " + userName);
            log.info("password: " + password);
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(url);
            dataSource.setUsername(userName);
            dataSource.setPassword(password);
            dataSource.setDriverClassName(driverClassName);
            return dataSource;
        }
    
    }
    

    TaskController

    package com.yipeng.task.demo.controller;
    
    import com.yipeng.task.demo.model.po.Students;
    import com.yipeng.task.demo.model.po.Teachers;
    import com.yipeng.task.demo.model.vo.TeachersAndStudentCountVo;
    import com.yipeng.task.demo.model.vo.TeachersVo;
    import com.yipeng.task.demo.service.StudentService;
    import com.yipeng.task.demo.service.TeacherService;
    import com.yipeng.task.demo.utils.RestResponse;
    
    
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import javax.annotation.Resource;
    
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:36
     */
    @Slf4j
    @RestController
    @RequestMapping("/task")
    public class TaskController {
    
        @Resource
        private StudentService studentService;
    
        @Resource
        private TeacherService teacherService;
    
        /**
         * 初始数据
         * 暂且执行一次,本例没有进行老师学生的去重。
         * 朋友们,可以想想两张表唯一索引的设计
         * @return
         */
        @GetMapping("/insertStudents")
        public RestResponse<Boolean> insertStudents() {
            Students studentZhang = new Students("男", "张三");
            Students studentLi = new Students("男", "李四");
            Students studentWang = new Students("女", "王二");
            Teachers teachersLi = new Teachers("李老师");
            Teachers teachersLiu = new Teachers("刘老师");
            Teachers teachersZhang = new Teachers("张老师");
            Teachers teachersZhao = new Teachers("赵老师");
            Teachers teachersWang = new Teachers("王老师");
            Teachers teachersFu = new Teachers("付老师");
            Set<Teachers> teachers = new HashSet<>();
            teachers.add(teachersLi);
            teachers.add(teachersLiu);
            teachers.add(teachersZhang);
            teachers.add(teachersZhao);
            teachers.add(teachersWang);
            teachers.add(teachersFu);
            studentZhang.setTeachers(teachers);
            studentLi.setTeachers(teachers);
            studentWang.setTeachers(teachers);
            List<Students> studentsList = new ArrayList<>();
            studentsList.add(studentZhang);
            studentsList.add(studentLi);
            studentsList.add(studentWang);
            teacherService.saveTeachers(new ArrayList<>(teachers));
            studentService.saveStudents(studentsList);
            return RestResponse.buildSuccess(Boolean.TRUE);
        }
    
        /**
         * 获取全部学生信息
         * @return
         */
        @GetMapping("/getStudentsInfo")
        public RestResponse<List<Students>> getStudentsInfo() {
            return RestResponse.buildSuccess(studentService.getStudents());
        }
    
        /**
         * 获取全部教师信息
         * @return
         */
        @GetMapping("/getTeachersInfo")
        public RestResponse<List<Teachers>> getTeachersInfo() {
            return RestResponse.buildSuccess(teacherService.getTeachers());
        }
    
        /**
         * 获取全部教师信息及学生数量
         * @return
         */
        @GetMapping("/getTeachers")
        public RestResponse<List<TeachersAndStudentCountVo>> getTeachersAndStudentsCount() {
            return RestResponse.buildSuccess(teacherService.getTeachersAndStudentsCount());
        }
    
        /**
         * 通过id获取老师姓名
         * @param id
         * @return
         */
        @GetMapping("/teachers/{id}")
        public RestResponse<TeachersVo> getTeachersById(@PathVariable Long id) {
            return RestResponse.buildSuccess(teacherService.getTeachersById(id));
        }
    
        /**
         * 删除学生信息
         * @param id
         * @return
         */
        @DeleteMapping("/students/delete/{id}")
        public RestResponse<Boolean> deleteStudentsById(@PathVariable Long id) {
            studentService.deleteStudentsById(id);
            return RestResponse.buildSuccess(Boolean.TRUE);
        }
    
        /**
         * 删除教师信息
         * @param id
         * @return
         */
        @DeleteMapping("/teachers/delete/{id}")
        public RestResponse<Boolean> deleteTeachersById(@PathVariable Long id) {
            teacherService.deleteTeachersById(id);
            return RestResponse.buildSuccess(Boolean.TRUE);
        }
    
    }
    

    BasePo

    package com.yipeng.task.demo.model.po;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:31
     */
    public abstract class BasePo {
    
    }
    

    Students

     

    package com.yipeng.task.demo.model.po;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    import java.io.Serializable;
    import java.util.Set;
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:30
     */
    @Data
    @Entity
    @Table(name = "students")
    @NoArgsConstructor
    public class Students extends BasePo implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String name;
    
        private String gender;
    
        @JsonIgnoreProperties(value = { "students" })
        @ManyToMany(cascade = CascadeType.PERSIST)
        @JoinTable(name="teachers_students" , joinColumns={@JoinColumn(name="sid", referencedColumnName = "id")}, inverseJoinColumns={@JoinColumn(name="tid", referencedColumnName = "id")})
        private Set<Teachers> teachers;
    
        // 仅作测试用途
        public Students(String name, String gender) {
            this.name = name;
            this.gender = gender;
        }
    
    }
    

    Teachers

    package com.yipeng.task.demo.model.po;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    import java.io.Serializable;
    import java.util.Set;
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.NoArgsConstructor;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:31
     */
    
    @Data
    @Entity
    @Table(name = "teachers")
    @EqualsAndHashCode(exclude = {"students"}, callSuper = false)
    @NoArgsConstructor
    public class Teachers extends BasePo implements Serializable {
    
        private static final long serialVersionUID = 2L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(length=8)
        private Long id;
    
        private String tname;
    
        @JsonIgnoreProperties(value = { "teachers" })
        @ManyToMany(cascade = CascadeType.PERSIST)
        @JoinTable(name="teachers_students" , joinColumns={@JoinColumn(name="tid", referencedColumnName = "id")}, inverseJoinColumns={@JoinColumn(name="sid", referencedColumnName = "id")})
        private Set<Students> students;
    
        // 仅作测试用途
        public Teachers(String tname) {
            this.tname = tname;
        }
    
    }
    

     

    TeachersAndStudentCountVo

    package com.yipeng.task.demo.model.vo;
    
    import lombok.Data;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 20:17
     */
    @Data
    public class TeachersAndStudentCountVo {
    
        private Long id;
    
        private String tname;
    
        private Integer count;
    
        public TeachersAndStudentCountVo() {}
    
        public TeachersAndStudentCountVo(Long id, String tname, Integer count) {
            this.id = id;
            this.tname = tname;
            this.count = count;
        }
    
    }
    

    TeachersImpl

    package com.yipeng.task.demo.model.vo;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 23:46
     */
    public interface  TeachersImpl {
    
        Long getId();
    
        String getTname();
    
        Integer getCount();
    
        default String toStringInfo() {
            return "id=" + getId() + "; tname=" + getTname() + "; count=" + getCount();
        }
    
    }
    

    TeachersVo

    package com.yipeng.task.demo.model.vo;
    
    import lombok.Data;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/30 0:04
     */
    @Data
    public class TeachersVo {
    
        private String tname;
    
        /**
         * 必须存在该构造函数
         * @param tname
         */
        public TeachersVo(String tname) {
            this.tname = tname;
        }
    
    }
    

    StudentService

    package com.yipeng.task.demo.service;
    
    import com.yipeng.task.demo.dao.StudentsDao;
    import com.yipeng.task.demo.model.po.Students;
    
    import java.util.List;
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:33
     */
    @Service
    public class StudentService {
    
        @Resource
        private StudentsDao studentsDao;
    
        public List<Students> getStudents() {
            return (List<Students>) studentsDao.findAll();
        }
    
        public void saveStudents(List<Students> studentsList) {
            studentsDao.saveAll(studentsList);
        }
    
        public void deleteStudentsById(Long id) {
            studentsDao.deleteById(id);
        }
    
    
    }

    TeacherService

    package com.yipeng.task.demo.service;
    
    import com.yipeng.task.demo.dao.TeachersDao;
    import com.yipeng.task.demo.model.po.Teachers;
    import com.yipeng.task.demo.model.vo.TeachersImpl;
    import com.yipeng.task.demo.model.vo.TeachersAndStudentCountVo;
    import com.yipeng.task.demo.model.vo.TeachersVo;
    import com.yipeng.task.demo.utils.PoVoUtils;
    
    import java.util.List;
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:33
     */
    @Service
    public class TeacherService {
    
        @Resource
        private TeachersDao teachersDao;
    
        public List<Teachers> getTeachers() {
            return (List<Teachers>) teachersDao.findAll();
        }
    
        public void saveTeachers(List<Teachers> teachersList) {
            teachersDao.saveAll(teachersList);
        }
    
        public void deleteTeachersById(Long id) {
            teachersDao.deleteById(id);
        }
    
        public TeachersVo getTeachersById(Long id) {
            TeachersVo teachersVo = teachersDao.getTeachersById(id);
            return teachersVo;
        }
    
        /**
         * 方法一: JPA自动获取(一般情况推荐)
         * @return
         */
        public List<TeachersAndStudentCountVo> getTeachersAndStudentsCount() {
            List<Teachers> teachersList = teachersDao.findAll();
            return PoVoUtils.transferList(teachersList, TeachersAndStudentCountVo.class, (p, v) -> {
                v.setCount(p.getStudents() == null ? 0 : p.getStudents().size());
            });
        }
    
        /**
         * 方法二:原生SQL 接口获取(超复杂情况推荐)
         */
        public List<TeachersAndStudentCountVo> getTeachersAndStudentsCountOne() {
            List<TeachersImpl> teachersList = teachersDao.getTeachersAndStudentsCountOne();
            return PoVoUtils.transferList(teachersList, TeachersAndStudentCountVo.class, (p, v) -> {
                v.setId(p.getId());
                v.setTname(p.getTname());
                v.setCount(p.getCount());
            });
        }
    
        /**
         * 方法三:原生SQL Object获取(了解,不推荐)
         */
        public List<TeachersAndStudentCountVo> getTeachersAndStudentsCountTwo() {
            List<Object[]> teachersList = teachersDao.getTeachersAndStudentsCountTwo();
            return PoVoUtils.transferList(teachersList, TeachersAndStudentCountVo.class, (p, v) -> {
                v.setId(Long.parseLong(p[0].toString()));
                v.setTname(p[1].toString());
                v.setCount(Integer.parseInt(p[2].toString()));
            });
        }
    
    }

    PoVoUtils

    package com.yipeng.task.demo.utils;
    
    import java.util.LinkedList;
    import java.util.List;
    
    import org.springframework.beans.BeanUtils;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/30 0:37
     */
    public class PoVoUtils {
    
        public static <T1, T2> T2 transfer(T1 from, Class<T2> toClass, String... ignoreProperties) {
            return transfer(from, toClass, null, ignoreProperties);
        }
    
        public static <T1, T2> T2 transfer(T1 from, Class<T2> toClass, AfterTransfer<T1, T2> at, String... ignoreProperties) {
            T2 to;
            try {
                to = toClass.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            BeanUtils.copyProperties(from, to, ignoreProperties);
            if (at != null) {
                at.apply(from, to);
            }
            return to;
        }
    
        public static <P, V> List<V> transferList(List<P> list, Class<V> vclass, AfterTransfer<P, V> afterPo2Vo) {
            List<V> lst = null;
            if (list != null && list.size() > 0) {
                List<V> flst = new LinkedList<>();
                list.forEach(p -> {
                    V v = transfer(p, vclass, afterPo2Vo);
                    flst.add(v);
                });
                lst = flst;
            }
    
            return lst;
        }
    
        public static interface AfterTransfer<T1, T2> {
            void apply(T1 t1, T2 t2);
        }
    
    }
    

    RestResponse

    package com.yipeng.task.demo.utils;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:55
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class RestResponse<T> {
    
        private int code;
        private String message;
        private T data;
    
        public static <T> RestResponse<T> buildErr(T data, String msg) {
            RestResponse<T> respResult = new RestResponse<>();
            respResult.setCode(0);
            respResult.setData(data);
            respResult.setMessage(msg);
            return respResult;
        }
    
        public static <T> RestResponse<T> buildSuccess(T data) {
            RestResponse<T> respResult = new RestResponse<>();
            respResult.setCode(1);
            respResult.setData(data);
            respResult.setMessage("ok");
            return respResult;
        }
    
    }
    

    TaskDemoApplication

    package com.yipeng.task.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * @Author: yipeng
     * @Date: 2021/4/29 15:21
     */
    @SpringBootApplication
    public class TaskDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TaskDemoApplication.class, args);
        }
    
    }
    

     

    分析部分

    报错分析:

    声明一点,@Transactional与@Modifying注解不适合查询类操作。本人第一次学习期间,手欠给写过来之后,会报以下错误。(Caused by: java.sql.SQLException: Can not issue executeUpdate() or executeLargeUpdate() for SELECTs)。


    很明显,用了executeUpdate() or executeLargeUpdate()方法,应该使用execute()方法,不懂的可以去查,在此不做赘述。

     

    @ManyToMany相关配置

    学生类:
    @JsonIgnoreProperties(value = { "students" })
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name="teachers_students" , joinColumns={@JoinColumn(name="sid", referencedColumnName = "id")}, inverseJoinColumns={@JoinColumn(name="tid", referencedColumnName = "id")})
    private Set<Teachers> teachers;
    教师类:
    @JsonIgnoreProperties(value = { "teachers" })
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name="teachers_students" , joinColumns={@JoinColumn(name="tid", referencedColumnName = "id")}, inverseJoinColumns={@JoinColumn(name="sid", referencedColumnName = "id")})
    private Set<Students> students;

    分析:

    1. 通过JsonIgnoreProperties注解,而不是@JsonIgnore

    网上有很多人建议使用@JsonIgnore注解,这个注解可以解决死循环,但是使用之后,会变成ManyToOne了。(即通过学生,可以查询到教师集合,但是通过教师,查询不到学生集合)

    学生类上使用@JsonIgnoreProperties(value = { "students" })表示循环回来后忽略students集合;

    教师类上使用@JsonIgnoreProperties(value = { "teachers" })表示循环回来后忽略teachers集合;

    该方法可以解决死循环问题

    2. 即便是ManyToMany,两者之间也有一个主动方,这里是学生为主动方

     

    初始数据接口

    curl --location --request GET '127.0.0.1:8888/task/insertStudents'

    执行一次即可,本例没有进行老师学生的去重。(朋友们,可以想想两张表唯一索引的设计)

     

    获取全部学生信息接口

    curl --location --request GET '127.0.0.1:8888/task/getStudentsInfo'

    返回结果:

    {"code":1,"message":"ok","data":[{"id":1,"name":"男","gender":"张三","teachers":[{"id":5,"tname":"张老师"},{"id":4,"tname":"付老师"},{"id":6,"tname":"赵老师"},{"id":1,"tname":"李老师"},{"id":2,"tname":"刘老师"},{"id":3,"tname":"王老师"}]},{"id":2,"name":"男","gender":"李四","teachers":[{"id":5,"tname":"张老师"},{"id":4,"tname":"付老师"},{"id":6,"tname":"赵老师"},{"id":1,"tname":"李老师"},{"id":2,"tname":"刘老师"},{"id":3,"tname":"王老师"}]},{"id":3,"name":"女","gender":"王二","teachers":[{"id":5,"tname":"张老师"},{"id":4,"tname":"付老师"},{"id":6,"tname":"赵老师"},{"id":1,"tname":"李老师"},{"id":2,"tname":"刘老师"},{"id":3,"tname":"王老师"}]}]}

    获取全部教师信息

    curl --location --request GET '127.0.0.1:8888/task/getTeachersInfo'

    返回结果:

    {"code":1,"message":"ok","data":[{"id":1,"tname":"李老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]},{"id":2,"tname":"刘老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]},{"id":3,"tname":"王老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]},{"id":4,"tname":"付老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]},{"id":5,"tname":"张老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]},{"id":6,"tname":"赵老师","students":[{"id":2,"name":"男","gender":"李四"},{"id":3,"name":"女","gender":"王二"},{"id":1,"name":"男","gender":"张三"}]}]}

    由以上两个接口,可以得知,不存在死循环,且学生和老师可以相互获得各自的信息。

     

    获取全部教师信息及学生数量

    curl --location --request GET '127.0.0.1:8888/task/getTeachers'

    返回结果:

    {"code":1,"message":"ok","data":[{"id":1,"tname":"李老师","count":3},{"id":2,"tname":"刘老师","count":3},{"id":3,"tname":"王老师","count":3},{"id":4,"tname":"付老师","count":3},{"id":5,"tname":"张老师","count":3},{"id":6,"tname":"赵老师","count":3}]}

    分析:

    这里我提供了三种方法:

    1. findAll(),这里能看出来JPA的强大,简单的逻辑,处理起来十分方便。

    2. List<TeachersImpl> getTeachersAndStudentsCountOne(),采用TeachersImpl接口接收复杂SQL的结果。这种方式在复杂繁琐的情况下可以使用,效果显著,需要掌握。

    3. List<Object[]> getTeachersAndStudentsCountTwo()方法,采用List<Object[]>接收复杂SQL的结果。这种方法做一般了解。

    PS:相关注意事项都写在代码里。

     

    通过id获取老师姓名接口

    curl --location --request GET '127.0.0.1:8888/task/teachers/6'

    返回结果:

    {"code":1,"message":"ok","data":{"tname":"赵老师"}}

    分析:

    /**
     * 此时记得
     * 不要添加 nativeQuery = true
     * 末尾不要写";"
     * @param id
     * @return
     */
    @Query(value = "select new com.yipeng.task.demo.model.vo.TeachersVo(tname) from Teachers where id =?1")
    TeachersVo getTeachersById(Long id);

    这里主要是想给朋友们提供一个思路,可以通过new com.yipeng.task.demo.model.vo.TeachersVo的方式接收部分数据。

    注意:

    1. 不要添加 nativeQuery = true

    2. 末尾不要写";"

    3. new com.yipeng.task.demo.model.vo.TeachersVo必须是全局限定名,且对应的TeachersVo类中必须有相关参数构造器。

     

    删除学生信息(重点)

    curl --location --request DELETE '127.0.0.1:8888/task/students/delete/1'

    返回结果:

    {"code":1,"message":"ok","data":true}

    删除前数据

     

    删除后结果:

    很显然,结果为我们想要的。

     

    删除教师信息(重点)

    curl --location --request DELETE '127.0.0.1:8888/task/teachers/delete/1'

    返回结果:

    {"code":1,"message":"ok","data":true}

    删除后结果:

    很显然,结果为我们想要的。

     

    代码:

     

    https://github.com/Longziling/task-demo.git

     

    结语:

     

    感谢你能读到这里。

    本次提供完整代码,旨在为初中级的朋友们理解JPA提供点帮助。

    具体讲解了@ManyToMany注解的使用,@ManyToMany注解死循环的处理(@JsonIgnoreProperties),

    复杂的原生SQL如何通过@Query注解实现定制化查询,如何用自定义类接收@Query注解的返回值及相关注意事项。

    欢迎评论。

     

     

     

     

     

     

    展开全文
  • SpringBoot-SpringData-ManyToMany,博文:http://blog.csdn.net/ABAP_Brave/article/details/52849827
  • 趟坑 ManyToMany 循环引用 死循环 栈内存溢出问题
  • JPA 多对多@ManyToMany查询数据时的死循环问题 使用JPA多对多,查询的时候死循环造成栈溢出 主表User @Data @Entity @Table(name = "tb_user") public class User { @Id @GenericGenerator(name = "idGenerator", ...

    JPA 多对多@ManyToMany查询数据时的死循环问题

    使用JPA多对多,查询的时候死循环造成栈溢出

    主表User

    @Data
    @Entity
    @Table(name = "tb_user")
    public class User  {
        @Id
        @GenericGenerator(name = "idGenerator", strategy = "uuid")
        @GeneratedValue(generator = "idGenerator")
        private String id;
    
        @Column(name = "username", unique = true, nullable = false, length = 64)
        private String username;
    
        @Column(name = "password", nullable = false, length = 64)
        private String password;
    
        @Column(name = "email", length = 64)
        private String email;
    
        @Enumerated(EnumType.STRING)
        private Gender gender;
    
        @Column(name = "create_time")
        @Temporal(TemporalType.TIMESTAMP)
        private Date createTime;
    
        @ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER, cascade = CascadeType.ALL)
        @JoinTable(name = "tb_user_role",
                //joinColumns,当前对象在中间表中的外键
                joinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "id")},
                //inverseJoinColumns,对方对象在中间表的外键
                inverseJoinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "id")})
        private Set<Role> roles = new HashSet<>();
        @Override
        public int hashCode() {
            return super.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);
        }
    
        @Override
        public String toString() {
            return super.toString();
        }
    }
    
    

    从表Role

    @Data
    @Entity
    @Table(name = "tb_role")
    @EqualsAndHashCode(exclude = {"userSet"})
    public class Role  {
    
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Long id;
    
    
        @Column(name = "role_name")
        private String roleName;
    
        /**
         * 配置用户到角色的多对多关系
         * 配置多对多的映射关系
         * 1.声明表关系的配置
         *
         * @ManyToMany(targetEntity = User.class)  //多对多
         * targetEntity:代表对方的实体类字节码
         * 2.配置中间表(包含两个外键)
         * @JoinTable name : 中间表的名称
         * joinColumns:配置当前对象在中间表的外键
         * @JoinColumn的数组 name:外键名
         * referencedColumnName:参照的主表的主键名
         * inverseJoinColumns:配置对方对象在中间表的外键
         */
         /*  @ManyToMany(targetEntity = User.class)
            @JoinTable(name = "sys_user_role",
                //joinColumns,当前对象在中间表中的外键
                joinColumns = {@JoinColumn(name = "tb_role_id", referencedColumnName = "role_id")},
                //inverseJoinColumns,对方对象在中间表的外键
                inverseJoinColumns = {@JoinColumn(name = "tb_user_id", referencedColumnName = "user_id")})*/
        @ManyToMany(mappedBy = "roles")
        private Set<User> users = new HashSet<>();
    
        @Override
        public int hashCode() {
            return super.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);
        }
    
        @Override
        public String toString() {
            return super.toString();
        }
    }
    

    findAll的结果可以看到是user和role是一层套一层的不停循环最后造成栈溢出问题
    在这里插入图片描述

    在这里插入图片描述

    原因分析

    查询User时,包含了Role属性roles,Role中又需要查询除user属性users

    解决办法一

    在Role的users字段加上@JsonIgnore注解

        @ManyToMany(mappedBy = "roles")
        @JsonIgnore
        private Set<User> users = new HashSet<>();
    
    解决办法二

    双向关联改为单向关联,只保留User的关联关系
    删掉Role中的关联

        @ManyToMany(mappedBy = "roles")
        private Set<User> users = new HashSet<>();
    

    但是查询role的时候就无法同时查询到关联的user了

    findAll的结果
    在这里插入图片描述

    展开全文
  • 用于映射多对多关系的 Hibernate 注释
  • api 3.0.0 org.glassfish javax.el 3.0.0 org.slf4j slf4j-api ${slf4j.version} org.apache.logging.log4j log4j-api ${log4j.version} junit junit 4.12 创建配置文件 update 创建实体类 注解说明 @ManyToMany ...
  • JPA @ManytoMany注解使用

    2022-01-18 23:48:34
    1. 两个实体类创建多对多的关系(如上图) 如果我们只需要维护中间表,也就是主键之间的关系,则不需要添加在属性中添加cascade = CascadeType.ALL.....(如果需要级联操作,也就是一个表的操作会影响到另一个表中的数据,...
  • Django的manytomany字段

    2021-04-30 11:58:47
    manytomany字段 用于表示多对多的关系,最常见的就是老师和班级的例子 一个老师可以教多个班级,一个班级也可以有多个班级 add 添加关系 teachers=models.Teacher.objects.filter(id__lt=1) #小于3 #通过对象...
  • @ManyToMany(多对多关系)使用小结

    千次阅读 2020-08-04 19:08:47
    @ManyToMany(多对多关系)使用小结! DeviceGroup类: package com.sunwave.grouping.domain; import javax.persistence.*; import java.io.Serializable; import java.util.List; @Entity @Table(name = "device_...
  • new_list = [c2, c3] s.elective.set(new_list, clear=True) 上半部是帶大家了解 ManyToMany 的基本應用,那下半部要教大家自訂中間表 ! 建立模型 信用卡與辦卡人 from django.db import models # Create your ...
  • 今天小编就为大家分享一篇基于django ManyToMany 使用的注意事项详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 今天在项目中使用jpa的manyToMany时遇到一个问题。 进行多对多查询时,抛出一个异常:org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ...
  • @ManyToMany

    千次阅读 2019-12-23 17:48:07
    @ManyToMany( cascade = { CascadeType.PERSIST, CascadeType.MERGE }) @JoinTable(name = "TEACHER_TO_STUDENT", joinColumns = { @JoinColumn(name = "TEACHER_ID") }, inverseJoinColumns = { @JoinColumn(name....
  • 这里写自定义目录标题概述假设模型添加数据解决方案 概述 Spring Data Jpa底层默认使用...系统每隔一段时间,会全量加载所有配置信息,刷新实例本地的内存配置,由于使用了@ManyToMany、@ManyToOne、@OneToMany等注解,
  • ManyToMany 级联保存问题 部门——人员关系 manyTomany 用户为关系维护方 部门实体 @Getter @Setter @Entity @Table(name = “departments”) @EntityListeners(AuditingEntityListener.class) public class ...
  • * @ORM\ManyToMany(targetEntity="App\Entity\Event", inversedBy="participants") */ private $workshops; </code></pre> <p>Entity Event.php file :</p> <pre><code>/** * @ORM\ManyToMany(targetEntity=...
  • C#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_ManyToManyC#例子代码 A0623_EF_...
  • Django2.2 model中manytomany 关联自己时,如果不设置symmetrical=False时,就会出现对称创建,即创建两个关联关系就会创建四条数据. class UserTest2(models.Model): name = models.CharField(max_length=128, null=...
  • Django下ManytoMany 增删改查 建立学生类老师类多对多关系,用ManytoManyField,迁移同步后自动生成中间表tb_Teacher_Student # 学生类 class Student(models.Model): name = models.CharField(max_length=32) ...
  • SpringBoot + JPA @ManyToMany 操作要点

    千次阅读 2019-09-19 15:03:12
    这里写自定义目录标题SpringBoot + JPA @ManyToMany 要点对应的Entity的建立。此处注意不可使用lombok @Data 注解。使用@Setter 、@Getter注解。主要原因时要自己覆写hash() equals(),toString() 方法。这样添加和...
  • 主篇:Django之Model操作之多对多...ManyToMany(Django提供的三种多对多方式)  1.自动创建多对多关联(django自动维护第三张表)  建立表类  表数据(django自带的sqlite可视化进行添加,当然也可...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,723
精华内容 5,089
关键字:

ManyToMany