由于项目中用了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注解的返回值及相关注意事项。
欢迎评论。