SpringBoot2.x 集成 SpringDataJpa
SpringDataJpa简介:Spring Data JPA是Spring Data家族的一员,可以轻松实现基于JPA的存储库。该模块处理对基于JPA的数据访问层的增强支持。这使得构建使用数据访问技术的Spring应用程序变得更加容易。
在相当长的一段时间内,实现应用程序的数据访问层一直很麻烦。必须编写太多样板代码来执行简单查询以及执行分页和审计。Spring Data JPA旨在通过减少实际需要的工作量来显著改善数据访问层的实现。作为开发人员,你编写repository接口,包括自定义查询器方法,Spring将自动提供实现。
下面通过SpringBoot2.x集成SpringDataJpa并进行基本的使用测试,其中SpringBoot使用的
2.2.2.RELEASE版本。
一、引入依赖
<!-- Spring Data JPA的起步依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- mysql数据库驱动依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok插件 可选(推荐) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency> <!-- 单元测试依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
二、编写配置文件
resources目录下新建
application.yml配置文件,配置内容如下:
spring: #mysql数据库连接信息 datasource: #高版本mysql驱动 driver-class-name: com.mysql.cj.jdbc.Driver #使用com.mysql.cj.jdbc.Driver驱动需要带上时区serverTimezone url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root #jpa相关配置 jpa: #数据库使用mysql database: mysql #控制台是否显示sql语句,true:显示 false:不显示 show-sql: true #是否根据实体类(@Entity注解的类)自动建表,true:是 false:否,默认为false generate-ddl: true hibernate: #自动创建或更新或验证数据库表结构,省略不会自动建表 #create:每次加载Hibernate时都会删除上一次生成的表,然后根据Entity类再重新来生成新表,即使两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因 #create-drop:每次加载Hibernate时根据Entity类生成表,但是sessionFactory一关闭,表就自动删除 #update:最常用的属性,第一次加载Hibernate时根据Entity类会自动建立起表的结构(前提是先建立好数据库),以后加载Hibernate时根据Entity类自动更新表结构, #即使表结构改变了,但表中的行仍然存在,不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会 #validate:每次加载Hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值 ddl-auto: update #设置数据库存储引擎为InnoDB database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
三、创建实体类并配置映射关系
package com.rtxtitanv.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.*; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.User * @description 用户实体类 * @date 2019/12/31 18:49 */ @Accessors(chain = true) //lombok注解,支持链式编程 @AllArgsConstructor //lombok注解,自动生成全参构造器 @NoArgsConstructor //lombok注解,自动生成无参构造器 @Data //lombok注解,自动生成get、set、toString等方法 @Entity //声明实体类 @Table(name = "user") //建立实体类与表的映射关系 public class User { @Id //声明该实例域为主键 @GeneratedValue(strategy = GenerationType.IDENTITY) //主键生成策略,IDENTITY:由数据库自行管理 //建立实例域id与表中字段id的映射关系 @Column(name = "id") private Long id; //建立实例域uid和表中字段uid的映射关系,length:指定此字段的长度,只对字符串有效,不写默认为255 //unique:是否添加唯一约束,不写时默认为false,nullable:表示此字段是否允许为null @Column(name = "uid", length = 64, unique = true, nullable = false) private String uid; //建立实例域userName和表中字段user_name的映射关系 @Column(name = "user_name", length = 16, unique = true, nullable = false) private String userName; //建立实例域passWord和表中字段pass_word的映射关系 @Column(name = "pass_word", length = 16, nullable = false) private String passWord; //建立实例域nickName和表中字段nick_name的映射关系 @Column(name = "nick_name", length = 16) private String nickName; //建立实例域age和表中字段age的映射关系 @Column(name = "age") private Integer age; //建立实例域email和表中字段email的映射关系 @Column(name = "email", unique = true, nullable = false) private String email; //建立实例域tel和表中字段tel的映射关系 @Column(name = "tel", unique = true, nullable = false) private String tel; //建立实例域regTime和表中字段reg_time的映射关系 @Column(name = "reg_time", nullable = false) private String regTime; //该实例域并非一个到数据库表的字段的映射,ORM框架将忽略该域 @Transient private String addr; }
注意:
1.如果类中的实例域使用枚举类型时,我们想要数据库中存储的是枚举对应的String类型值,而不是枚举的索引值时,需要在实例域上面添加注解
@Enumerated(EnumType.STRING),例如:
@Enumerated(EnumType.STRING) @Column(name = "user_type") private UserType type;
2.如果实体类不在启动类所在包及其子包下,则需要在主启动类上加上以下注解:
@EntityScan(basePackages = {"包含实体类的包路径"})
如果有多个包路径,只需要在主启动类上加上以下注解:
@EntityScan(basePackages = {"包含实体类的包路径1", "包含实体类的包路径2", ..., "包含实体类的包路径n"})
四、创建Repository接口
package com.rtxtitanv.repository; import com.rtxtitanv.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.repository.UserRepository * @description UserRepository接口用于操作用户表,JpaRepository<实体类类型, 主键类型>:用于完成基本CRUD,分页,排序操作 * JpaSpecificationExecutor<实体类类型>:用于复杂查询 * @date 2019/12/31 19:31 */ public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
五、SpringDataJpa的使用测试
首先启动主启动类,会在数据库中建立一张user表,表中暂无数据,建表信息如下:
2020-01-01 13:00:24.612 INFO 16552 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect Hibernate: create table user (id bigint not null auto_increment, age integer, email varchar(255) not null, nick_name varchar(16), pass_word varchar(16) not null, reg_time varchar(255) not null, tel varchar(255) not null, uid varchar(64) not null, user_name varchar(16) not null, primary key (id)) engine=InnoDB Hibernate: alter table user drop index UK_ob8kqyqqgmefl0aco34akdtpe Hibernate: alter table user add constraint UK_ob8kqyqqgmefl0aco34akdtpe unique (email) Hibernate: alter table user drop index UK_nbfia2ok6c7at4i0er6uyskkx Hibernate: alter table user add constraint UK_nbfia2ok6c7at4i0er6uyskkx unique (tel) Hibernate: alter table user drop index UK_a7hlm8sj8kmijx6ucp7wfyt31 Hibernate: alter table user add constraint UK_a7hlm8sj8kmijx6ucp7wfyt31 unique (uid) Hibernate: alter table user drop index UK_lqjrcobrh9jc8wpcar64q1bfh Hibernate: alter table user add constraint UK_lqjrcobrh9jc8wpcar64q1bfh unique (user_name)
SpringDataJpa单元测试类
package com.rtxtitanv; import com.rtxtitanv.repository.UserRepository; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.JpaTest * @description SpringDataJpa单元测试类 * @date 2019/12/31 20:05 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = JpaApplication.class) public class JpaTest { @Autowired private UserRepository userRepository; private static Logger logger = LoggerFactory.getLogger(JpaTest.class); }
1.基本的增删改查
使用
JpaRepository接口自带的方法进行基本的增删改查。
(1)新增测试
①保存单条记录
/** * 保存5条测试数据,一次插入一条数据 * 使用方法 <S extends T> S save(S var1) * 实体中主键不存在时保存记录 */ @Test public void saveTest() { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String formattedDate = dateFormat.format(date); logger.info("保存5条测试数据开始"); userRepository.save(new User(null, "296ebeb46acd49aca54f0d5a5a1257c3", "qwer123", "123456", "aaa", 24, "qwer123@ss.com", "13915176512", formattedDate, "beijing")); userRepository.save(new User(null, "e6c133e338bb4b7c857be76104986acb", "asd6666", "qw23ss", "asc", 18, "asd6666@ss.com", "15736226963", formattedDate, "tianjin")); userRepository.save(new User(null, "179a91c205f84416b39347d714516c95", "tgh3er2", "11111", "r123er", 22, "advx@ss.com", "18956929863", formattedDate, "hangzhou")); userRepository.save(new User(null, "dddfa7b84b194ea5a62393ef8f211710", "erfgyhn", "567809a.", "rw23ed", 27, "ddfty@ss.com", "16389562477", formattedDate, "shanghai")); userRepository.save(new User(null, "7fc652d011e8448e99aee948f1af9187", "rty7ui81", "a2ef56.a", "asc", 18, "sdrtyhui@ss.com", "15966358996", formattedDate, "nanjing")); logger.info("保存5条测试数据结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 13:07:25.034 INFO 13332 --- [ main] com.rtxtitanv.JpaTest : 保存5条测试数据开始 Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) 2020-01-01 13:07:25.102 INFO 13332 --- [ main] com.rtxtitanv.JpaTest : 保存5条测试数据结束
user表中成功插入5条记录:
②批量保存
/** * 批量保存5条测试数据 后台执行时仍是一条一条插入 * 使用方法 <S extends T> List<S> saveAll(Iterable<S> var1) * 实体中主键不存在时保存记录 */ @Test public void saveInBatchTest() { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String formattedDate = dateFormat.format(date); User user1 = new User(null, "8960a15b37d0552aa84483e24fc57b80", "23erg", "2234rf", "aaa", 15, "qadfgyty@ss.com", "16895635789", formattedDate, "beijing"); User user2 = new User(null, "cc0106b175a6572e8d2967b3dd563193", "63serf", "2ww5t", "w323rrt", 36, "wer33@ss.com", "15766958245", formattedDate, "suzhou"); User user3 = new User(null, "7669890a99c1581483edf72fa48d702c", "2w4r", "3345tt", "aaa", 24, "qert23@ss.com", "19725689756", formattedDate, "wuhan"); User user4 = new User(null, "9a512c6ffe01565abb619e1199002603", "12er", "134rty", "aa23e54", 23, "qwer5@ss.com", "13858963144", formattedDate, "jinan"); User user5 = new User(null, "f7e05429074b5db9a85d623377475ced", "yu2sd", "1w0978", "asc", 31, "wer123@ss.com", "18741569832", formattedDate, "xian"); List<User> list = new ArrayList<>(); list.add(user1); list.add(user2); list.add(user3); list.add(user4); list.add(user5); logger.info("批量保存5条测试数据开始"); userRepository.saveAll(list); logger.info("批量保存5条测试数据结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 13:10:26.851 INFO 19312 --- [ main] com.rtxtitanv.JpaTest : 批量保存5条测试数据开始 Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into user (age, email, nick_name, pass_word, reg_time, tel, uid, user_name) values (?, ?, ?, ?, ?, ?, ?, ?) 2020-01-01 13:10:26.906 INFO 19312 --- [ main] com.rtxtitanv.JpaTest : 批量保存5条测试数据结束
user表中成功插入5条记录:
(2)查询测试
①查询所有记录
/** * 查询所有记录 * 使用方法 List<T> findAll(); */ @Test public void findAllTest() { logger.info("查询所有开始"); List<User> userList = userRepository.findAll(); if (userList.isEmpty()) { logger.info("不存在用户数据"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("查询所有结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 14:33:10.449 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : 查询所有开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=2, uid=e6c133e338bb4b7c857be76104986acb, userName=asd6666, passWord=qw23ss, nickName=asc, age=18, email=asd6666@ss.com, tel=15736226963, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=11111, nickName=r123er, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=4, uid=dddfa7b84b194ea5a62393ef8f211710, userName=erfgyhn, passWord=567809a., nickName=rw23ed, age=27, email=ddfty@ss.com, tel=16389562477, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=a2ef56.a, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=6, uid=8960a15b37d0552aa84483e24fc57b80, userName=23erg, passWord=2234rf, nickName=aaa, age=15, email=qadfgyty@ss.com, tel=16895635789, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=7, uid=cc0106b175a6572e8d2967b3dd563193, userName=63serf, passWord=2ww5t, nickName=w323rrt, age=36, email=wer33@ss.com, tel=15766958245, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=8, uid=7669890a99c1581483edf72fa48d702c, userName=2w4r, passWord=3345tt, nickName=aaa, age=24, email=qert23@ss.com, tel=19725689756, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=9, uid=9a512c6ffe01565abb619e1199002603, userName=12er, passWord=134rty, nickName=aa23e54, age=23, email=qwer5@ss.com, tel=13858963144, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : User(id=10, uid=f7e05429074b5db9a85d623377475ced, userName=yu2sd, passWord=1w0978, nickName=asc, age=31, email=wer123@ss.com, tel=18741569832, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:33:10.589 INFO 16768 --- [ main] com.rtxtitanv.JpaTest : 查询所有结束
②查询所有记录并排序
/** * 查询所有记录并排序 * 使用方法 List<T> findAll(Sort var1) */ @Test public void findAllAndSortTest() { Sort sort = Sort.by(Sort.Direction.DESC, "age"); logger.info("查询所有并按年龄降序排序开始"); List<User> userList = userRepository.findAll(sort); if (userList.isEmpty()) { logger.info("不存在用户数据"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("查询所有并按年龄降序排序结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 14:43:17.666 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : 查询所有并按年龄降序排序开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ order by user0_.age desc 2020-01-01 14:43:17.795 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=7, uid=cc0106b175a6572e8d2967b3dd563193, userName=63serf, passWord=2ww5t, nickName=w323rrt, age=36, email=wer33@ss.com, tel=15766958245, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=10, uid=f7e05429074b5db9a85d623377475ced, userName=yu2sd, passWord=1w0978, nickName=asc, age=31, email=wer123@ss.com, tel=18741569832, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=4, uid=dddfa7b84b194ea5a62393ef8f211710, userName=erfgyhn, passWord=567809a., nickName=rw23ed, age=27, email=ddfty@ss.com, tel=16389562477, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=8, uid=7669890a99c1581483edf72fa48d702c, userName=2w4r, passWord=3345tt, nickName=aaa, age=24, email=qert23@ss.com, tel=19725689756, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=9, uid=9a512c6ffe01565abb619e1199002603, userName=12er, passWord=134rty, nickName=aa23e54, age=23, email=qwer5@ss.com, tel=13858963144, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=11111, nickName=r123er, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=2, uid=e6c133e338bb4b7c857be76104986acb, userName=asd6666, passWord=qw23ss, nickName=asc, age=18, email=asd6666@ss.com, tel=15736226963, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=a2ef56.a, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : User(id=6, uid=8960a15b37d0552aa84483e24fc57b80, userName=23erg, passWord=2234rf, nickName=aaa, age=15, email=qadfgyty@ss.com, tel=16895635789, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 14:43:17.796 INFO 14168 --- [ main] com.rtxtitanv.JpaTest : 查询所有并按年龄降序排序结束
③分页查询所有记录
/** * 分页查询所有记录 * 使用方法 Page<T> findAll(Pageable var1) */ @Test public void findAllAndPageTest() { Sort sort = Sort.by(Sort.Direction.ASC, "age"); Pageable pageable = PageRequest.of(2, 3, sort); logger.info("分页查询所有开始"); Page<User> page = userRepository.findAll(pageable); if (page.isEmpty()) { logger.info("不存在分页数据"); } else { logger.info("总条数:" + page.getTotalElements()); logger.info("总页数:" + page.getTotalPages()); List<User> userList = page.getContent(); userList.forEach(user -> logger.info(user.toString())); } logger.info("分页查询所有结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 15:29:59.803 INFO 560 --- [ main] com.rtxtitanv.JpaTest : 分页查询所有开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ order by user0_.age asc limit ?, ? Hibernate: select count(user0_.id) as col_0_0_ from user user0_ 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : 总条数:10 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : 总页数:4 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : User(id=8, uid=7669890a99c1581483edf72fa48d702c, userName=2w4r, passWord=3345tt, nickName=aaa, age=24, email=qert23@ss.com, tel=19725689756, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : User(id=4, uid=dddfa7b84b194ea5a62393ef8f211710, userName=erfgyhn, passWord=567809a., nickName=rw23ed, age=27, email=ddfty@ss.com, tel=16389562477, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : User(id=10, uid=f7e05429074b5db9a85d623377475ced, userName=yu2sd, passWord=1w0978, nickName=asc, age=31, email=wer123@ss.com, tel=18741569832, regTime=2020-01-01 13:10:26, addr=null) 2020-01-01 15:29:59.937 INFO 560 --- [ main] com.rtxtitanv.JpaTest : 分页查询所有结束
④根据id查询
/** * 根据id查询 * 使用方法 Optional<T> findById(ID var1) */ @Test public void findByIdTest() { logger.info("根据id查询开始"); Optional<User> userOptional = userRepository.findById(1L); if (!userOptional.isPresent()) { logger.info("查询的用户不存在"); } else { User user = userOptional.get(); String userInfo = user.toString(); logger.info(userInfo); } logger.info("根据id查询结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 15:35:32.801 INFO 16380 --- [ main] com.rtxtitanv.JpaTest : 根据id查询开始 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? 2020-01-01 15:35:32.845 INFO 16380 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 15:35:32.845 INFO 16380 --- [ main] com.rtxtitanv.JpaTest : 根据id查询结束
⑤根据id批量查询
/** * 根据id批量查询 * 使用方法 List<T> findAllById(Iterable<ID> var1) */ @Test public void findInBatchByIdTest() { List<Long> ids = new ArrayList<>(); ids.add(1L); ids.add(3L); ids.add(5L); logger.info("根据id批量查询开始"); List<User> userList = userRepository.findAllById(ids); if (userList.isEmpty()) { logger.info("查询的用户不存在"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("根据id批量查询结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 15:40:54.836 INFO 16764 --- [ main] com.rtxtitanv.JpaTest : 根据id批量查询开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.id in (? , ? , ?) 2020-01-01 15:40:54.978 INFO 16764 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 15:40:54.978 INFO 16764 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=11111, nickName=r123er, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 15:40:54.978 INFO 16764 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=a2ef56.a, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 15:40:54.978 INFO 16764 --- [ main] com.rtxtitanv.JpaTest : 根据id批量查询结束
(3)修改测试
①修改单条记录
/** * 修改单条记录 * 使用方法 <S extends T> S save(S var1) * 当实体中主键存在时先根据主键查询再根据主键更新 */ @Test public void updateOneTest() { Optional<User> userOptional = userRepository.findById(3L); if (!userOptional.isPresent()) { logger.info("该用户不存在"); } else { User user = userOptional.get(); logger.info("修改前的记录: " + user.toString()); logger.info("修改一条用户记录开始"); user.setNickName("6wer23a").setPassWord("123123"); userRepository.save(user); logger.info("修改一条用户记录结束"); logger.info("修改后的记录: " + userRepository.findById(3L).get().toString()); } }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 16:25:56.783 INFO 13144 --- [ main] com.rtxtitanv.JpaTest : 修改前的记录: User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=11111, nickName=r123er, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 16:25:56.783 INFO 13144 --- [ main] com.rtxtitanv.JpaTest : 修改一条用户记录开始 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: update user set age=?, email=?, nick_name=?, pass_word=?, reg_time=?, tel=?, uid=?, user_name=? where id=? 2020-01-01 16:25:56.809 INFO 13144 --- [ main] com.rtxtitanv.JpaTest : 修改一条用户记录结束 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? 2020-01-01 16:25:56.812 INFO 13144 --- [ main] com.rtxtitanv.JpaTest : 修改后的记录: User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=123123, nickName=6wer23a, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null)
②批量修改
/** * 批量修改 * 使用方法 <S extends T> List<S> saveAll(Iterable<S> var1) * 当实体中主键存在时先根据主键查询再根据主键更新 */ @Test public void updateInBatchTest() { List<Long> ids = new ArrayList<>(); ids.add(1L); ids.add(3L); ids.add(5L); List<User> userList = userRepository.findAllById(ids); if (userList.isEmpty()) { logger.info("查询不到记录"); } else { logger.info("修改前的记录"); userList.forEach(user -> logger.info(user.toString())); logger.info("------------分割线-------------"); userList.forEach(user -> user.setPassWord("666666")); logger.info("批量修改开始"); userRepository.saveAll(userList); logger.info("批量修改结束"); logger.info("修改后的记录"); userRepository.findAllById(ids).forEach(user -> logger.info(user.toString())); } }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 17:13:10.356 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : 修改前的记录 2020-01-01 17:13:10.357 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 17:13:10.357 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=123123, nickName=6wer23a, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 17:13:10.357 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=a2ef56.a, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 17:13:10.357 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : ------------分割线------------- 2020-01-01 17:13:10.357 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : 批量修改开始 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: update user set age=?, email=?, nick_name=?, pass_word=?, reg_time=?, tel=?, uid=?, user_name=? where id=? Hibernate: update user set age=?, email=?, nick_name=?, pass_word=?, reg_time=?, tel=?, uid=?, user_name=? where id=? Hibernate: update user set age=?, email=?, nick_name=?, pass_word=?, reg_time=?, tel=?, uid=?, user_name=? where id=? 2020-01-01 17:13:10.388 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : 批量修改结束 2020-01-01 17:13:10.389 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : 修改后的记录 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.id in (? , ? , ?) 2020-01-01 17:13:10.392 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=666666, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 17:13:10.392 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=666666, nickName=6wer23a, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 13:07:25, addr=null) 2020-01-01 17:13:10.392 INFO 11612 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=666666, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 13:07:25, addr=null)
(4)删除测试
①根据id删除
/** * 根据id删除 * 使用方法 void deleteById(ID var1) */ @Test public void deleteByIdTest() { logger.info("根据id删除开始"); userRepository.deleteById(3L); logger.info("根据id删除结束"); }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表id为3的记录已成功删除。
2020-01-01 17:55:00.006 INFO 17744 --- [ main] com.rtxtitanv.JpaTest : 根据id删除开始 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: delete from user where id=? 2020-01-01 17:55:00.068 INFO 17744 --- [ main] com.rtxtitanv.JpaTest : 根据id删除结束
②删除单条记录
/** * 删除单条记录 * 使用方法 void delete(T var1) */ @Test public void deleteOneTest() { Optional<User> userOptional = userRepository.findById(1L); if (!userOptional.isPresent()) { logger.info("该用户不存在"); } else { User user = userOptional.get(); logger.info("删除一条用户记录开始"); userRepository.delete(user); logger.info("删除一条用户记录结束"); } }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表id为1的记录已成功删除。
2020-01-01 18:06:10.505 INFO 7772 --- [ main] com.rtxtitanv.JpaTest : 删除一条用户记录开始 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.email as email3_0_0_, user0_.nick_name as nick_nam4_0_0_, user0_.pass_word as pass_wor5_0_0_, user0_.reg_time as reg_time6_0_0_, user0_.tel as tel7_0_0_, user0_.uid as uid8_0_0_, user0_.user_name as user_nam9_0_0_ from user user0_ where user0_.id=? Hibernate: delete from user where id=? 2020-01-01 18:06:10.530 INFO 7772 --- [ main] com.rtxtitanv.JpaTest : 删除一条用户记录结束
③批量删除
/** * 批量删除 * 使用方法 void deleteInBatch(Iterable<T> var1) * 后台执行时生成一条sql语句删除 */ @Test public void deleteInBatch() { List<Long> ids = new ArrayList<>(); ids.add(2L); ids.add(4L); ids.add(5L); ids.add(7L); List<User> userList = userRepository.findAllById(ids); if (userList.isEmpty()) { logger.info("用户数据不存在"); } else { logger.info("批量删除开始"); //后台执行时一条一条删除 //userRepository.deleteAll(userList); //后台执行时生成一条sql语句删除 userRepository.deleteInBatch(userList); logger.info("批量删除结束"); } }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表id为2、4、5、7的记录已成功删除。
2020-01-01 18:13:44.060 INFO 7796 --- [ main] com.rtxtitanv.JpaTest : 批量删除开始 Hibernate: delete from user where id=? or id=? or id=? or id=? 2020-01-01 18:13:44.075 INFO 7796 --- [ main] com.rtxtitanv.JpaTest : 批量删除结束
④删除所有记录
/** * 删除所有记录 * 使用方法 void deleteAllInBatch() * 后台执行时生成一条sql语句删除 */ @Test public void deleteAllTest() { //后台执行时一条一条删除 //userRepository.deleteAll(); //后台执行时生成一条sql语句删除 logger.info("删除所有开始"); userRepository.deleteAllInBatch(); logger.info("删除所有结束"); }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表所有记录已成功删除。
2020-01-01 18:26:09.704 INFO 17072 --- [ main] com.rtxtitanv.JpaTest : 删除所有开始 Hibernate: delete from user 2020-01-01 18:26:09.801 INFO 17072 --- [ main] com.rtxtitanv.JpaTest : 删除所有结束
2 4000 .方法命名规则的使用
SpringDataJPA支持接口方法命名规则自动生成sql语句来实现数据库操作,语法支持
findBy、
readBy、
queryBy、
countBy、
getBy、
deleteBy、
existsBy等后面跟属性名称,例如查询方法以
findBy开头,方法名称必须遵循驼峰式命名规则,涉及条件查询时,条件的属性用条件关键字连接,框架在进行方法名解析时,会先把方法名多余的前缀截取掉之后对剩下部分进行解析。
(1)查询测试
①按昵称查询
在UserRepository接口中添加自定义方法findByNickName:
/** * 方法命名规则之按昵称查询 * @param nickName 呢称 * @return 查询结果集 */ List<User> findByNickName(String nickName);
方法命名规则之按昵称查询测试方法:
/** * 方法命名规则之按昵称查询 */ @Test public void findByNickNameTest() { logger.info("方法命名规则之按昵称查询开始"); List<User> userList = userRepository.findByNickName("aaa"); if (userList.isEmpty()) { logger.info("昵称为aaa的用户不存在"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("方法命名规则之按昵称查询结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 22:51:22.898 INFO 16708 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称查询开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.nick_name=? 2020-01-01 22:51:23.035 INFO 16708 --- [ main] com.rtxtitanv.JpaTest : User(id=1, uid=296ebeb46acd49aca54f0d5a5a1257c3, userName=qwer123, passWord=123456, nickName=aaa, age=24, email=qwer123@ss.com, tel=13915176512, regTime=2020-01-01 22:50:34, addr=null) 2020-01-01 22:51:23.035 INFO 16708 --- [ main] com.rtxtitanv.JpaTest : User(id=6, uid=8960a15b37d0552aa84483e24fc57b80, userName=23erg, passWord=2234rf, nickName=aaa, age=15, email=qadfgyty@ss.com, tel=16895635789, regTime=2020-01-01 22:50:46, addr=null) 2020-01-01 22:51:23.035 INFO 16708 --- [ main] com.rtxtitanv.JpaTest : User(id=8, uid=7669890a99c1581483edf72fa48d702c, userName=2w4r, passWord=3345tt, nickName=aaa, age=24, email=qert23@ss.com, tel=19725689756, regTime=2020-01-01 22:50:46, addr=null) 2020-01-01 22:51:23.035 INFO 16708 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称查询结束
②按昵称和年龄查询
在UserRepository接口中添加自定义方法findByNickNameAndAge:
/** * 方法命名规则之按昵称和年龄查询 * @param nickName 昵称 * @param age 年龄 * @return 查询结果集 */ List<User> findByNickNameAndAge(String nickName, Integer age);
方法命名规则之按昵称和年龄查询测试方法:
/** * 方法命名规则之按昵称和年龄查询 */ @Test public void findByNickNameAndAgeTest() { logger.info("方法命名规则之按昵称和年龄查询开始"); List<User> userList = userRepository.findByNickNameAndAge("asc", 18); if (userList.isEmpty()) { logger.info("昵称为asc并且年龄为18的用户不存在"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("方法命名规则之按昵称和年龄查询结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 22:53:15.900 INFO 19376 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称和年龄查询开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.nick_name=? and user0_.age=? 2020-01-01 22:53:16.049 INFO 19376 --- [ main] com.rtxtitanv.JpaTest : User(id=2, uid=e6c133e338bb4b7c857be76104986acb, userName=asd6666, passWord=qw23ss, nickName=asc, age=18, email=asd6666@ss.com, tel=15736226963, regTime=2020-01-01 22:50:34, addr=null) 2020-01-01 22:53:16.049 INFO 19376 --- [ main] com.rtxtitanv.JpaTest : User(id=5, uid=7fc652d011e8448e99aee948f1af9187, userName=rty7ui81, passWord=a2ef56.a, nickName=asc, age=18, email=sdrtyhui@ss.com, tel=15966358996, regTime=2020-01-01 22:50:34, addr=null) 2020-01-01 22:53:16.049 INFO 19376 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称和年龄查询结束
③按昵称模糊查询
在UserRepository接口中添加自定义方法findByNickNameLike:
/** * 方法命名规则之按昵称模糊查询 * @param nickName 昵称 * @return 查询结果集 */ List<User> findByNickNameLike(String nickName);
方法命名规则之按昵称模糊查询测试方法:
/** * 方法命名规则之按昵称模糊查询 */ @Test public void findByNickNameLikeTest() { logger.info("方法命名规则之按昵称模糊查询开始"); List<User> userList = userRepository.findByNickNameLike("%23%"); if (userList.isEmpty()) { logger.info("昵称包含23的用户不存在"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("方法命名规则之按昵称模糊查询结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-01 22:54:21.566 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称模糊查询开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.nick_name like ? escape ? 2020-01-01 22:54:21.710 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : User(id=3, uid=179a91c205f84416b39347d714516c95, userName=tgh3er2, passWord=11111, nickName=r123er, age=22, email=advx@ss.com, tel=18956929863, regTime=2020-01-01 22:50:34, addr=null) 2020-01-01 22:54:21.710 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : User(id=4, uid=dddfa7b84b194ea5a62393ef8f211710, userName=erfgyhn, passWord=567809a., nickName=rw23ed, age=27, email=ddfty@ss.com, tel=16389562477, regTime=2020-01-01 22:50:34, addr=null) 2020-01-01 22:54:21.710 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : User(id=7, uid=cc0106b175a6572e8d2967b3dd563193, userName=63serf, passWord=2ww5t, nickName=w323rrt, age=36, email=wer33@s 20000 s.com, tel=15766958245, regTime=2020-01-01 22:50:46, addr=null) 2020-01-01 22:54:21.710 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : User(id=9, uid=9a512c6ffe01565abb619e1199002603, userName=12er, passWord=134rty, nickName=aa23e54, age=23, email=qwer5@ss.com, tel=13858963144, regTime=2020-01-01 22:50:46, addr=null) 2020-01-01 22:54:21.710 INFO 14148 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称模糊查询结束
④按年龄条件统计
在UserRepository接口中添加自定义方法countByAgeLessThanEqual:
/** * 方法命名规则之按年龄条件统计 * @param age 年龄 * @return 小于等于给定年龄的记录总数 */ Long countByAgeLessThanEqual(Integer age);
方法命名规则之按年龄条件统计测试方法:
/** * 方法命名规则之按年龄条件统计 */ @Test public void countByAgeLessThanEqualTest() { logger.info("方法命名规则之按年龄条件统计开始"); Long count = userRepository.countByAgeLessThanEqual(24); logger.info("年龄不超过24岁的用户总数: " + count); logger.info("方法命名规则之按年龄条件统计结束"); }
控制台打印的日志及自动生成的sql 语句如下:
2020-01-01 22:55:16.252 INFO 256 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按年龄条件统计开始 Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.age<=? 2020-01-01 22:55:16.393 INFO 256 --- [ main] com.rtxtitanv.JpaTest : 年龄不超过24岁的用户总数: 7 2020-01-01 22:55:16.393 INFO 256 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按年龄条件统计结束
⑤按邮箱模糊查询名称并按年龄升序
使用SpringDataJPA提供的Projections功能,创建一个接口NameOnly,充当一个“视图”的作用,可以用来自定义的封装我们需要查询的字段。
package com.rtxtitanv.projections; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.projections.NameOnly * @description 只查询name的projections * @date 2020/1/2 14:28 */ public interface NameOnly { String getUserName(); String getNickName(); }
在 UserRepository接口中添加自定义方法findByEmailContainingOrderByAgeAsc:
/** * 方法命名规则之按邮箱模糊查询名称并按年龄排序 * 只查询用户名和昵称 * @param email 邮箱 * @return List<NameOnly> 名称列表(projections接口NameOnly只包含用户名和昵称) */ List<NameOnly> findByEmailContainingOrderByAgeAsc(String email);
方法命名规则之按邮箱模糊查询名称并按年龄升序测试方法:
/** * 方法命名规则之按邮箱模糊查询名称并按年龄升序 */ @Test public void findByEmailContainingOrderByAgeTest() { logger.info("方法命名规则之按邮箱模糊查询名称并按年龄升序开始"); List<NameOnly> nameOnly = userRepository.findByEmailContainingOrderByAgeAsc("er"); if (nameOnly.isEmpty()) { logger.info("不存在满足条件记录"); } else { nameOnly.forEach(name -> logger.info("userName: " + name.getUserName() + ", " + "nickName: " + name.getNickName())); } logger.info("方法命名规则之按邮箱模糊查询名称并按年龄升序结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-02 15:01:22.010 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按邮箱模糊查询名称并按年龄升序开始 Hibernate: select user0_.user_name as col_0_0_, user0_.nick_name as col_1_0_ from user user0_ where user0_.email like ? escape ? order by user0_.age asc 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : userName: 12er, nickName: aa23e54 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : userName: qwer123, nickName: aaa 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : userName: 2w4r, nickName: aaa 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : userName: yu2sd, nickName: asc 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : userName: 63serf, nickName: w323rrt 2020-01-02 15:01:22.149 INFO 14736 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按邮箱模糊查询名称并按年龄升序结束
⑥限制查询
有时候我们只需要查询前N个元素,或者只取前一个实体。在UserRepository接口中添加如下自定义方法:
/** * 方法命名规则之限制查询 * @return 查询结果 */ User findFirstByOrderByAgeAsc(); User findTopByOrderByAgeAsc(); Page<User> queryFirst10ByNickName(String nickName, Pageable pageable); List<User> findFirst10ByNickName(String nickName, Sort sort); Page<User> findTop10ByNickName(String nickName, Pageable pageable);
方法命名规则之限制查询测试方法,这里只测试第一个按年龄升序:
/** * 方法命名规则之限制查询 */ @Test public void findFirstByOrderByAgeAscTest() { User user = userRepository.findFirstByOrderByAgeAsc(); if (user == null) { logger.info("用户数据不存在"); } else { String userInfo = user.toString(); logger.info(userInfo); } }
控制台打印的日志及自动生成的sql语句如下:
2020-01-07 16:38:03.900 INFO 18860 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之限制查询开始 Hibernate: select user0_.id as id1_5_, user0_.age as age2_5_, user0_.email as email3_5_, user0_.nick_name as nick_nam4_5_, user0_.pass_word as pass_wor5_5_, user0_.reg_time as reg_time6_5_, user0_.tel as tel7_5_, user0_.uid as uid8_5_, user0_.user_name as user_nam9_5_ from user user0_ order by user0_.age asc limit ? 2020-01-07 16:38:03.961 INFO 18860 --- [ main] com.rtxtitanv.JpaTest : User(id=6, uid=8960a15b37d0552aa84483e24fc57b80, userName=23erg, passWord=2234rf, nickName=aaa, age=15, email=qadfgyty@ss.com, tel=16895635789, regTime=2020-01-07 16:36:18, addr=null) 2020-01-07 16:38:03.961 INFO 18860 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之限制查询结束
(2)删除测试
①按昵称(忽略大小写)删除
在UserRepository接口中添加自定义方法deleteByNickNameIgnoreCase:
/** * 方法命名规则之按昵称(忽略大小写)删除 * @param nickName 昵称 * @return 删除的记录数 */ //@Transactional:开启事务支持 @Transactional(rollbackFor = Exception.class) int deleteByNickNameIgnoreCase(String nickName);
方法命名规则之按昵称(忽略大小写)删除测试方法:
/** * 方法命名规则之按昵称(忽略大小写)删除 */ @Test public void deleteByNickNameIgnoreCaseTest() { logger.info("方法命名规则之按昵称删除开始"); int result = userRepository.deleteByNickNameIgnoreCase("AAa"); logger.info("总共删除了" + result + "条记录"); logger.info("方法命名规则之按昵称删除结束"); }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表昵称为AAa(忽略大小写)的所有记录已成功删除。
2020-01-02 16:15:44.746 INFO 6720 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称删除开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where upper(user0_.nick_name)=upper(?) Hibernate: delete from user where id=? Hibernate: delete from user where id=? Hibernate: delete from user where id=? 2020-01-02 16:15:44.902 INFO 6720 --- [ main] com.rtxtitanv.JpaTest : 总共删除了3条记录 2020-01-02 16:15:44.902 INFO 6720 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按昵称删除结束
②按年龄批量删除
在UserRepository 接口中添加自定义方法deleteByAgeIn:
/** * 方法命名规则之按年龄批量删除 * @param ages 年龄列表 * @return 删除的记录数 */ @Transactional(rollbackFor = Exception.class) int deleteByAgeIn(List<Integer> ages);
方法命名规则之按年龄批量删除测试方法:
/** * 方法命名规则之按年龄批量删除 */ @Test public void deleteByAgeInTest() { List<Integer> ages = new ArrayList<>(); ages.add(18); ages.add(23); ages.add(30); logger.info("方法命名规则之按年龄批量删除开始"); int result = userRepository.deleteByAgeIn(ages); logger.info("总共删除了" + result + "条记录"); logger.info("方法命名规则之按年龄批量删除结束"); }
控制台打印的日志及自动生成的sql语句如下,navicat刷新user表年龄为18、23、30的所有记录已成功删除。
2020-01-02 16:35:57.844 INFO 11048 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按年龄批量删除开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.age in (? , ? , ?) Hibernate: delete from user where id=? Hibernate: delete from user where id=? Hibernate: delete from user where id=? 2020-01-02 16:35:57.992 INFO 11048 --- [ main] com.rtxtitanv.JpaTest : 总共删除了3条记录 2020-01-02 16:35:57.993 INFO 11048 --- [ main] com.rtxtitanv.JpaTest : 方法命名规则之按年龄批量删除结束
(3)方法命名规则支持的关键字
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1 |
SpringDataJPA 2.2.3.RELEASE 官方文档
3.JPQL和SQL方式实现增删改查
SpringDataJPA支持使用JPQL和SQL的方式完成对数据库的操作。
(1)新增测试
在UserRepository接口中添加自定义方法insertUser:
/** * 使用sql语句插入一条记录 * @param uid uid * @param userName 用户名 * @param passWord 密码 * @param nickName 昵称 * @param age 年龄 * @param email 邮箱 * @param tel 手机 * @param regTime 注册时间 * @return 返回1表示插入成功 */ //nativeQuery:是否使用本地sql,true表示使用本地sql,缺省默认值为false,不使用本地sql //?占位符后的数字对应方法中的参数索引,从1开始 @Query(value = "insert into user(uid,user_name,pass_word,nick_name,age,email,tel,reg_time)" + " values(?1,?2,?3,?4,?5,?6,?7,?8)", nativeQuery = true) //@Modifying:该注解标识该操作为一个插入更新删除操作,框架最终不会生成一个查询操作 @Modifying @Transactional(rollbackFor = Exception.class) int insertUser(String uid, String userName, String passWord, String nickName, Integer age, String email, String tel, String regTime);
sql语句插入测试方法:
/** * 使用sql语句插入一条记录 */ @Test public void insertUserTest() { logger.info("sql插入一条记录开始"); int result = userRepository.insertUser("f0ff89db-aa72-55dc-aaba-e1ec11fa2fec", "dfgyytvb2", "123456", "rty235", 18, "miopl@ss.com", "1256698463", "2020-01-02 11:51:16"); logger.info("sql插入一条记录结束"); if (result == 1) { logger.info("插入成功"); } else { logger.info("插入失败"); } }
控制台打印的日志及自动生成的sql语句如下:
2020-01-02 18:25:39.676 INFO 4224 --- [ main] com.rtxtitanv.JpaTest : sql插入一条记录开始 Hibernate: insert into user(uid,user_name,pass_word,nick_name,age,email,tel,reg_time) values(?,?,?,?,?,?,?,?) 2020-01-02 18:25:39.734 INFO 4224 --- [ main] com.rtxtitanv.JpaTest : sql插入一条记录结束 2020-01-02 18:25:39.734 INFO 4224 --- [ main] com.rtxtitanv.JpaTest : 插入成功
(2)查询测试
①JPQL语句按年龄批量查询名称并排序
在UserRepository接口中添加自定义方法findNameByAgeIn:
/** * 使用JPQL语句按年龄批量查询名称并排序 * 只查询用户名和昵称 * @param ageList 年龄列表 * @param sort 排序参数 * @return List<NameOnly> 名称列表(projections接口NameOnly只包含用户名和昵称) */ //:为占位符,#{#Object} SpEL表达式将对象传递进sql语句 //此处有坑,注意一定要加别名,并且与实体类的实例域名一致,否则NameOnly实现中封装不进数据,查出来的值为null @Query("select u.userName as userName, u.nickName as nickName from User u where u.age in :#{#ageList}") List<NameOnly> findNameByAgeIn(List<Integer> ageList, Sort sort);
JPQL按年龄批量查询名称并排序测试方法:
/** * 使用JPQL语句按年龄批量查询名称并排序 */ @Test public void findNameByAgeInTest() { Sort sort = Sort.by("userName").descending(); List<Integer> ages = new ArrayList<>(); ages.add(18); ages.add(24); ages.add(27); logger.info("JPQL按年龄批量查询名称并按用户名降序开始"); List<NameOnly> names = userRepository.findNameByAgeIn(ages, sort); if (names.isEmpty()) { logger.info("满足条件记录不存在"); } else { names.forEach(name -> logger.info("userName: " + name.getUserName() + ", " + "nickName: " + name.getNickName())); } logger.info("JPQL按年龄批量查询名称并按用户名降序结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-02 21:08:38.419 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : JPQL按年龄批量查询名称并按用户名降序开始 Hibernate: select user0_.user_name as col_0_0_, user0_.nick_name as col_1_0_ from user user0_ where user0_.age in (? , ? , ?) order by col_0_0_ desc 2020-01-02 21:08:38.509 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : userName: rty7ui81, nickName: asc 2020-01-02 21:08:38.509 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : userName: qwer123, nickName: aaa 2020-01-02 21:08:38.510 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : userName: erfgyhn, nickName: rw23ed 2020-01-02 21:08:38.510 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : userName: asd6666, nickName: asc 2020-01-02 21:08:38.510 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : userName: 2w4r, nickName: aaa 2020-01-02 21:08:38.510 INFO 11124 --- [ main] com.rtxtitanv.JpaTest : JPQL按年龄批量查询名称并按用户名降序结束
②sql语句按用户名和昵称模糊分页查询名称
在UserRepository接口添加自定义方法findNameByNickNameAndUserNameLike:
/** * 使用sql语句按用户名和昵称模糊分页查询名称 * 只查询用户名和昵称 * @param nickName 昵称 * @param userName 用户名 * @param pageable 分页参数 * @return List<NameOnly> 名称列表(projections接口NameOnly只包含用户名和昵称) */ //@Param注解的值与:占位符后的字符串一致,用于参数传递 @Query(value = "select u.nick_name as nickName, u.user_name as userName from user u where u.nick_name like %:nickname% and u.user_name like %:username%", countQuery = "select count(u.nick_name) from user u where u.nick_name like %:nickname% and u.user_name like %:username%", nativeQuery = true) Page<NameOnly> findNameByNickNameAndUserNameLike(@Param("nickname") String nickName, @Param("username") String userName, Pageable pageable);
sql语句按用户名和昵称模糊分页查询名称测试方法:
/** * 使用sql语句按用户名和昵称模糊分页查询名称 */ @Test public void findNameByNickNameAndUserNameLikeTest() { Pageable pageable = PageRequest.of(1,2); logger.info("sql语句按用户名和昵称模糊分页查询名称开始"); Page<NameOnly> names = userRepository.findNameByNickNameAndUserNameLike("a", "r", pageable); if (names.isEmpty()) { logger.info("满足条件的查询记录不存在"); } else { logger.info("总条数: " + names.getTotalElements()); logger.info("总页数: " + names.getTotalPages()); names.getContent().forEach(name -> logger.info("userName: " + name.getUserName() + ", " + "nickName: " + name.getNickName())); } logger.info("sql语句按用户名和昵称模糊分页查询名称结束"); }
控制台打印的日志及自动生成的sql语句如下:
2020-01-02 21:53:24.733 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : sql语句按用户名和昵称模糊分页查询名称开始 Hibernate: select u.nick_name as nickName, u.user_name as userName from user u where u.nick_name like ? and u.user_name like ? limit ?, ? Hibernate: select count(u.nick_name) from user u where u.nick_name like ? and u.user_name like ? 2020-01-02 21:53:24.813 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : 总条数: 5 2020-01-02 21:53:24.813 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : 总页数: 3 2020-01-02 21:53:24.814 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : userName: 23erg, nickName: aaa 2020-01-02 21:53:24.815 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : userName: 2w4r, nickName: aaa 2020-01-02 21:53:24.815 INFO 7120 --- [ main] com.rtxtitanv.JpaTest : sql语句按用户名和昵称模糊分页查询名称结束
(3)修改测试
①JPQL语句根据年龄更新昵称
在UserRepository接口添加自定义方法updateUserNameByAge:
/** * 使用JPQL语句根据年龄更新昵称 * @param nickName 昵称 * @param age 年龄 * @return 更新的记录数 */ @Query("update User u set u.nickName = ?1 where u.age = ?2") @Modifying @Transactional(rollbackFor = Exception.class) int updateUserNameByAge(String nickName, Integer age);
JPQL语句根据年龄更新昵称测试方法:
/** * 使用JPQL语句根据年龄更新昵称 */ @Test public void updateUserNameByAgeTest() { logger.info("JPQL根据年龄更新昵称开始"); int result = userRepository.updateUserNameByAge("nickname-01", 18); logger.info("更新了"+ result + "条记录"); logger.info("JPQL根据年龄更新昵称结束"); }
控制台打印的日志及自动生成的sql语句如下,刷新user表发现更新成功。
2020-01-02 22:33:57.543 INFO 13984 --- [ main] com.rtxtitanv.JpaTest : JPQL根据年龄更新昵称开始 Hibernate: update user set nick_name=? where age=? 2020-01-02 22:33:57.581 INFO 13984 --- [ main] com.rtxtitanv.JpaTest : 更新了2条记录 2020-01-02 22:33:57.581 INFO 13984 --- [ main] com.rtxtitanv.JpaTest : JPQL根据年龄更新昵称结束
②sql语句更新单条记录
在UserRepository接口添加自定义方法updateUser:
/** * 使用sql语句更新单条记录 * @param user 用户对象 * @return */ //使用SpEL表达式传递对象属性至sql语句 @Query(value = "update user u set u.uid = :#{#user.uid}, u.user_name = :#{#user.userName}," + " u.pass_word = :#{#user.passWord}, u.nick_name = :#{#user.nickName}," + " u.email = :#{#user.email}, u.age = :#{#user.age}," + " u.tel = :#{#user.tel}, u.reg_time = :#{#user.regTime} where u.id = :#{#user.id}", nativeQuery = true) @Modifying @Transactional(rollbackFor = Exception.class) int updateUser(@Param("user") User user);
sql语句更新单条记录测试方法:
/** * 使用sql语句更新单条记录 */ @Test public void updateUserTest() { Optional<User> userOptional = userRepository.findById(1L); if (!userOptional.isPresent()) { logger.info("查询用户不存在"); } else { User user = userOptional.get(); user.setPassWord("654321").setNickName("ava"); logger.info("sql语句更新单条记录开始"); userRepository.updateUser(user); logger.info("sql语句更新单条记录结束"); } }
控制台打印的日志及自动生成的sql语句如下,刷新user表发现更新成功。
2020-01-02 23:01:51.505 INFO 18876 --- [ main] com.rtxtitanv.JpaTest : sql语句更新单条记录开始 Hibernate: update user u set u.uid = ?, u.user_name = ?, u.pass_word = ?, u.nick_name = ?, u.email = ?, u.age = ?, u.tel = ?, u.reg_time = ? where u.id = ? 2020-01-02 23:01:51.570 INFO 18876 --- [ main] com.rtxtitanv.JpaTest : sql语句更新单条记录结束
(4)删除测试
在UserRepository接口添加自定义方法deleteInBacthById:
/** * 使用JPQL语句根据id批量删除 * @param ids * @return 删除记录数 */ //#{#entityName} 默认为实体类的名称,如果使用了@Entity(name = "xxx")来注解实体类 //#{#entityName}的值为xxx @Query("delete from #{#entityName} u where u.id in ?1") @Modifying @Transactional(rollbackFor = Exception.class) int deleteInBacthById(List<Long> ids);
JPQL语句根据id批量删除测试方法:
/** * 使用JPQL语句根据id批量删除 */ @Test public void deleteInBacthByIdTest() { List<Long> ids = new ArrayList<>(); ids.add(1L); ids.add(3L); ids.add(6L); ids.add(9L); logger.info("使用JPQL语句根据id批量删除开始"); int result = userRepository.deleteInBacthById(ids); logger.info("总共删除了" + result + "条记录"); logger.info("使用JPQL语句根据id批量删除结束"); }
控制台打印的日志及自动生成的sql语句如下,刷新user表发现成功删除4条记录。
2020-01-02 23:36:49.732 INFO 20096 --- [ main] com.rtxtitanv.JpaTest : 使用JPQL语句根据id批量删除开始 Hibernate: delete from user where id in (? , ? , ? , ?) 2020-01-02 23:36:49.773 INFO 20096 --- [ main] com.rtxtitanv.JpaTest : 总共删除了4条记录 2020-01-02 23:36:49.773 INFO 20096 --- [ main] com.rtxtitanv.JpaTest : 使用JPQL语句根据id批量删除结束
4.Specifications动态查询
SpringDataJpa提供了
JpaSpecificationExecutor接口,该接口主要用于动态的条件查询,支持分页和排序。
以下是
JpaSpecificationExecutor接口中的方法,
Specification接口主要用于封装查询条件。
public interface JpaSpecificationExecutor<T> { //根据条件查询返回单个对象 Optional<T> findOne(@Nullable Specification<T> var1); //根据条件查询返回List集合 List<T> findAll(@Nullable Specification<T> var1); //根据条件分页查询 Page<T> findAll(@Nullable Specification<T> var1, Pageable var2); //根据条件排序查询 List<T> findAll(@Nullable Specification<T> var1, Sort var2); //根据条件统计查询 long count(@Nullable Specification<T> var1); }
(1)条件查询
①单条件查询
/** * JpaSpecificationExecutor单条件查询测试 */ @Test public void findByOneConditionTest() { logger.info("单条件查询测试开始"); /** * root:查询的根对象,可以通过get方法获取实体属性 * criteriaQuery:代表一个顶层查询对象,可以构建自定义查询,包含select、where、orderBy、groupBy等 * criteriaBuilder:查询条件构造器 */ List<User> userList = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> { //user_name = "qwer123" Predicate predicate1 = criteriaBuilder.equal(root.get("userName"), "qwer123"); //email like %er% Predicate predicate2 = criteriaBuilder.like(root.get("email"), "%er%"); //age between 15 and 25 Predicate predicate3 = criteriaBuilder.between(root.get("age"), 15, 25); //age >= 18 Predicate predicate4 = criteriaBuilder.ge(root.get("age"), 18); //age <= 25 Predicate predicate5 = criteriaBuilder.le(root.get("age"), 25); return predicate5; }); if (userList.isEmpty()) { logger.info("没有满足条件的数据"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("单条件查询测试结束"); }
②多条件查询
用于封装查询条件的类:
package com.rtxtitanv.model.query; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.List; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.query.UserQuery * @description 封装用户查询条件类 * @date 2020/1/3 15:23 */ @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor @Data public class UserQuery { private Long idQuery; private String userNameQuery; private String nickNameQuery; private String passWordQuery; private String emailQuery; private String telQuery; private Integer minAgeQuery; private Integer maxAgeQuery; private List<Long> idsQuery; private List<Integer> agesQuery; }
动态拼接多条件查询测试方法:
/** * JpaSpecificationExecutor动态拼接多条件查询测试 * 拼接方式1:每个条件均为and连接 * 实际开发中可以根据实际的动态条件灵活处理 */ @Test public void findByConditionsTest() { //手动模拟查询条件 //UserQuery userQuery = null; UserQuery userQuery = new UserQuery(); List<Long> ids = new ArrayList<>(); List<Integer> ages = new ArrayList<>(); ids.add(1L); ids.add(2L); ids.add(5L); ages.add(18); ages.add(24); userQuery.setUserNameQuery("r").setNickNameQuery("a") .setMinAgeQuery(18).setMaxAgeQuery(27).setIdQuery(3L) .setTelQuery("135").setEmailQuery("rt").setPassWordQuery("123") .setIdsQuery(ids).setAgesQuery(ages); logger.info("动态拼接多条件查询测试开始"); List<User> userList = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> { //如果userQuery为空或者userQuery所有属性均为空会自动生成where 1 = 1 Predicate predicate = criteriaBuilder.conjunction(); //如果userQuery为空或者userQuery所有属性均为空会查询所有记录 if (userQuery != null) { if (userQuery.getIdQuery() != null) { //notEqual查询 predicate.getExpressions().add(criteriaBuilder.notEqual(root.get("id"), userQuery.getIdQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getUserNameQuery())) { //like查询 predicate.getExpressions().add(criteriaBuilder.like(root.get("userName"), "%" + userQuery.getUserNameQuery() + "%")); } if (!StringUtils.isNullOrEmpty(userQuery.getNickNameQuery())) { //notLike查询 predicate.getExpressions().add(criteriaBuilder.notLike(root.get("nickName"), "%" + userQuery.getNickNameQuery() + "%")); } if (!StringUtils.isNullOrEmpty(userQuery.getPassWordQuery())) { //equal查询 predicate.getExpressions().add(criteriaBuilder.equal(root.get("passWord"), userQuery.getPassWordQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getEmailQuery())) { //notEqual查询 predicate.getExpressions().add(criteriaBuilder.notEqual(root.get("email"), userQuery.getEmailQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getTelQuery())) { //like查询 predicate.getExpressions().add(criteriaBuilder.like(root.get("tel"), "%" + userQuery.getTelQuery())); } if (userQuery.getMinAgeQuery() != null && userQuery.getMaxAgeQuery() != null) { //between查询 predicate.getExpressions().add(criteriaBuilder.between(root.get("age"), userQuery.getMinAgeQuery(), userQuery.getMaxAgeQuery())); } else if (userQuery.getMinAgeQuery() != null) { //>=查询 gt为> predicate.getExpressions().add(criteriaBuilder.ge(root.get("age"), userQuery.getMinAgeQuery())); } else if (userQuery.getMaxAgeQuery() != null) { //<=查询 lt为< predicate.getExpressions().add(criteriaBuilder.le(root.get("age"), userQuery.getMaxAgeQuery())); } if (userQuery.getIdsQuery() != null && !userQuery.getIdsQuery().isEmpty()) { //in 批量查询 predicate.getExpressions().add(criteriaBuilder.and(root.get("id").in(userQuery.getIdsQuery()))); } if (userQuery.getAgesQuery() != null && !userQuery.getAgesQuery().isEmpty()) { predicate.getExpressions().add(criteriaBuilder.and(root.get("age").in(userQuery.getAgesQuery()))); } } return predicate; }); if (userList.isEmpty()) { logger.info("查询不到满足条件的数据"); } else { userList.forEach(user -> logger.info(user.toString())); } logger.info("动态拼接多条件查询测试结束"); }
所有查询条件都不为空时自动生成的sql语句如下:
2020-01-03 17:35:05.236 INFO 19832 --- [ main] com.rtxtitanv.JpaTest : 动态拼接多条件查询测试开始 Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where user0_.id<>3 and (user0_.user_name like ?) and (user0_.nick_name not like ?) and user0_.pass_word=? and user0_.email<>? and (user0_.tel like ?) and (user0_.age between 18 and 27) and (user0_.id in (1 , 2 , 5)) and (user0_.age in (18 , 24)) 2020-01-03 17:35:05.290 INFO 19832 --- [ main] com.rtxtitanv.JpaTest : 查询不到满足条件的数据 2020-01-03 17:35:05.290 INFO 19832 --- [ main] com.rtxtitanv.JpaTest : 动态拼接多条件查询测试结束
查询条件为空时生成
where 1=1如下:
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.nick_name as nick_nam4_0_, user0_.pass_word as pass_wor5_0_, user0_.reg_time as reg_time6_0_, user0_.tel as tel7_0_, user0_.uid as uid8_0_, user0_.user_name as user_nam9_0_ from user user0_ where 1=1
动态拼接多条件查询测试方法2:
/** * JpaSpecificationExecutor动态拼接多条件查询测试 * 拼接方式2:与方式1效果相同 */ @Test public void findByConditionsTest2() { //模拟查询条件此处省略 List<User> userList = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> { List<Predicate> predicates = new ArrayList<>(); //sql语句会自动生成where 1 = 1 predicates.add(criteriaBuilder.conjunction()); //如果userQuery为空或者userQuery所有属性均为空会查询所有记录 if (userQuery != null) { if (userQuery.getIdQuery() != null) { //notEqual查询 predicates.add(criteriaBuilder.notEqual(root.get("id"), userQuery.getIdQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getUserNameQuery())) { //like查询 predicates.add(criteriaBuilder.like(root.get("userName"), "%" + userQuery.getUserNameQuery() + "%")); } if (!StringUtils.isNullOrEmpty(userQuery.getNickNameQuery())) { //notLike查询 predicates.add(criteriaBuilder.notLike(root.get("nickName"), "%" + userQuery.getNickNameQuery() + "%")); } if (!StringUtils.isNullOrEmpty(userQuery.getPassWordQuery())) { //equal查询 predicates.add(criteriaBuilder.equal(root.get("passWord"), userQuery.getPassWordQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getEmailQuery())) { //notEqual查询 predicates.add(criteriaBuilder.notEqual(root.get("email"), userQuery.getEmailQuery())); } if (!StringUtils.isNullOrEmpty(userQuery.getTelQuery())) { //like查询 predicates.add(criteriaBuilder.like(root.get("tel"), "%" + userQuery.getTelQuery())); } if (userQuery.getMinAgeQuery() != null && userQuery.getMaxAgeQuery() != null) { //between查询 predicates.add(criteriaBuilder.between(root.get("age"), userQuery.getMinAgeQuery(), userQuery.getMaxAgeQuery())); } else if (userQuery.getMinAgeQuery() != null) { //>=查询 gt为> predicates.add(criteriaBuilder.ge(root.get("age"), userQuery.getMinAgeQuery())); } else if (userQuery.getMaxAgeQuery() != null) { //<=查询 lt为< predicates.add(criteriaBuilder.le(root.get("age"), userQuery.getMaxAgeQuery())); } if (userQuery.getIdsQuery() != null && !userQuery.getIdsQuery().isEmpty()) { //in 批量查询 predicates.add(criteriaBuilder.and(root.get("id").in(userQuery.getIdsQuery()))); } if (userQuery.getAgesQuery() != null && !userQuery.getAgesQuery().isEmpty()) { predicates.add(criteriaBuilder.and(root.get("age").in(userQuery.getAgesQuery()))); } } /** * Predicate and(Predicate... 4000 var1):and连接查询条件,可传入Predicate[] and连接数组内所有条件 * Predicate or(Predicate... var1):or连接查询条件,可传入Predicate[] or连接数组内所有条件 * 此处所有条件均为and连接,如果有更复杂的条件连接,比如: * where ((a=? and b=? and c=?) or (e=? and f=?) or g=?) and x=? and y=? * 先and连接abc、ef,再or连接abc、ef和g作为一个条件z,再and连接xyz */ return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])); }); //打印日志测试此处省略
(2)分页查询
/** * 多条件排序分页查询 */ @Test public void findByConditionsPageAndSortTest() { //模拟查询条件此处省略 //定义排序规则 先按age降序,再按tel升序,再按id降序 Sort.Order orderAge = new Sort.Order(Sort.Direction.DESC, "age"); Sort.Order orderTel = new Sort.Order(Sort.Direction.ASC, "tel"); Sort.Order orderId = new Sort.Order(Sort.Direction.DESC, "id"); Sort sort = Sort.by(orderAge, orderTel, orderId); //定义分页参数,由于是测试第几页和每页记录数写死 int pageNum = 3; int pageSize = 3; //jpa分页从0页开始,页码需要-1 Pageable pageable = PageRequest.of(pageNum - 1 , pageSize, sort); logger.info("多条件排序分页查询测试开始"); Page<User> page = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> { //此处封装查询条件省略 }, pageable); if (page.isEmpty()) { logger.info("查询不到满足条件的数据"); } else { logger.info("总条数:" + page.getTotalElements()); logger.info("总页数:" + page.getTotalPages()); List<User> userList = page.getContent(); userList.forEach(user -> logger.info(user.toString())); } logger.info("多条件排序分页查询测试结束"); }
自动生成的sql语句的排序及分页部分如下:
order by user0_.age desc, user0_.tel asc, user0_.id desc limit ?, ?
5.多表关联操作
(1)一对多关联
①创建实体类并配置映射
package com.rtxtitanv.model; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.HashSet; import java.util.Set; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.Account * @description 账户实体类 OneToMany 一对多的一方 * @date 2020/1/3 20:34 */ @Setter @Getter @Entity @Table(name = "account") public class Account { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "account_id") private Long accountId; @Column(name = "account_name", unique = true, nullable = false) private String accountName; @Column(name = "account_password", nullable = false) private String accountPassword; @Column(name = "account_alias") private String accountAlias; @Column(name = "account_addr") private String accountAddr; @Column(name = "account_tel", unique = true, nullable = false) private String accountTel; @Column(name = "account_rank", nullable = false) private Long accountRank; @Column(name = "account_location", nullable = false) private String accountLocation; @OneToMany(mappedBy = "account", //引用在多方实体类中一方实体类对象名称,一方放弃维护外键关系 //CascadeType为级联设置:操作一个对象同时操作它的关联对象 // PERSIST:保存,REFRESH:刷新,MERGE:合并,REMOVE:删除 //FetchType:设置加载方式,LAZY:延迟加载,EAGER:立即加载 //orphanRemoval:是否使用孤儿删除, 24000 即在一方关联多方的集合中移除的多方记录将 //成为孤儿,没有与一方任何一条记录关联,此时会自动删除这些多方记录,true:使用,false:不使用 cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.LAZY, orphanRemoval = true) private Set<Order> orders = new HashSet<>(); @Override public String toString() { return "Account{" + "accountId=" + accountId + ", accountName='" + accountName + '\'' + ", accountPassword='" + accountPassword + '\'' + ", accountAlias='" + accountAlias + '\'' + ", accountAddr='" + accountAddr + '\'' + ", accountTel='" + accountTel + '\'' + ", accountRank=" + accountRank + ", accountLocation='" + accountLocation + '\'' + '}'; } }
package com.rtxtitanv.model; import lombok.Getter; import lombok.Setter; import javax.persistence.*; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.Order * @description 订单实体类 ManyToOne 一对多的多方 * @date 2020/1/3 20:33 */ @Setter @Getter @Entity @Table(name = "orders") //注意表名不能为order,自动建表时会报错 public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "orders_id") private Long orderId; @Column(name = "orders_name", nullable = false) private String orderName; @Column(name = "orders_description") private String orderDescription; @Column(name = "orders_status", nullable = false) private String orderStatus; @Column(name = "orders_total_price", nullable = false) private String orderTotalPrice; @Column(name = "orders_item_count", nullable = false) private Integer orderItemCount; @Column(name = "orders_addr", nullable = false) private String orderAddr; //FetchType.EAGER 立即加载 @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) //定义主键字段和外键字段对应关系,name:外键字段名称,nullable:是否允许为空 @JoinColumn(name = "orders_account_id", nullable = false) private Account account; @Override public String toString() { return "Order{" + "orderId=" + orderId + ", orderName='" + orderName + '\'' + ", orderDescription='" + orderDescription + '\'' + ", orderStatus='" + orderStatus + '\'' + ", orderTotalPrice='" + orderTotalPrice + '\'' + ", orderItemCount=" + orderItemCount + ", orderAddr='" + orderAddr + '\'' + '}'; } }
②新增测试
新建AccountRepository和OrderRepository接口:
package com.rtxtitanv.repository; import com.rtxtitanv.model.Account; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.repository.AccountRepository * @description AccountRepository接口用于操作account表 * @date 2020/1/3 21:37 */ public interface AccountRepository extends JpaRepository<Account, Long>, JpaSpecificationExecutor<Account> { } --------------------------------------------------------------------------------------------------------------------------------- package com.rtxtitanv.repository; import com.rtxtitanv.model.Order; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** 1. @author rtxtitanv 2. @version v1.0.0 3. @name com.rtxtitanv.repository.OrderRepository 4. @description OrderRepository接口用于操作orders表 5. @date 2020/1/3 21:42 */ public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> { }
测试类中注入AccountRepository和OrderRepository:
@Autowired private AccountRepository accountRepository; @Autowired private OrderRepository orderRepository;
如果实体类使用了lombok插件中的
@Data自动生成getter、setter、hashCode以及toString方法,lombok会在生成时出现循环比较两类中的hashcode导致栈内存溢出,所以在多表关联时创建实体类不使用lombok的
@Data和
@ToString(使用
@ToString后续查询会出现循环调用toString方法,造成 StackOverflowError),改用
@Getter和
@Setter,自己手写toString方法,如果需要用到hashCode方法时需要自己手写。
java.lang.StackOverflowError at java.util.AbstractSet.hashCode(AbstractSet.java:122) at com.rtxtitanv.model.Account.hashCode(Account.java:20) at com.rtxtitanv.model.Order.hashCode(Order.java:16) at java.util.AbstractSet.hashCode(AbstractSet.java:126) at com.rtxtitanv.model.Account.hashCode(Account.java:20) at com.rtxtitanv.model.Order.hashCode(Order.java:16) at java.util.AbstractSet.hashCode(AbstractSet.java:126
保存测试:
/** * 多表关联一对多保存 */ @Transactional //开始事务支持 @Rollback(false) //设置不回滚 @Test public void oneToManySaveTest() { //封装保存数据 Account account = new Account(); account.setAccountName("rtx_titan_v"); account.setAccountPassword("123456"); account.setAccountAlias("cdf_dv"); account.setAccountRank(7L); account.setAccountTel("13313313311"); account.setAccountLocation("china"); account.setAccountAddr("北京西城区"); Order order1 = new Order(); Order order2 = new Order(); Order order3 = new Order(); order1.setOrderName("索尼ps5次世代游戏主机"); order1.setOrderDescription("索尼ps5,无索不玩"); order1.setOrderStatus("等待卖家发货"); order1.setOrderTotalPrice("5000"); order1.setOrderItemCount(1); order1.setOrderAddr("北京西城区"); order2.setOrderName("XBox Edit 2代"); order2.setOrderDescription("微软精英2代,无线蓝牙手柄国行"); order2.setOrderStatus("卖家已发货"); order2.setOrderTotalPrice("1390"); order2.setOrderItemCount(1); order2.setOrderAddr("北京西城区"); order3.setOrderName("XBox Edit 3代"); order3.setOrderDescription("微软精英3代,无线蓝牙手柄国行"); order3.setOrderStatus("卖家已发货"); order3.setOrderTotalPrice("1390"); order3.setOrderItemCount(1); order3.setOrderAddr("北京西城区"); //关联操作 account.getOrders().add(order1); account.getOrders().add(order2); order1.setAccount(account); order2.setAccount(account); //保存操作 //由于account和order实体设置了级联保存 //此处任意保存其中一个order,后台会自动保存order1、order2和关联的account //orderRepository.save(order1); //orderRepository.save(order2); //保存account,会自动保存关联的order1和order2 accountRepository.save(account); //此处为account关联order1和order2保存之后再关联一个新的order3保存 /*account.getOrders().add(order3); order3.setAccount(account); orderRepository.save(order3);*/ }
控制台打印的日志和自动生成的sql如下,navicat查看保存成功。
2020-01-04 15:43:33.559 INFO 8228 --- [ main] com.rtxtitanv.JpaTest : 保存开始 Hibernate: insert into account (account_addr, account_alias, account_location, account_name, account_password, account_rank, account_tel) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into orders (orders_account_id, orders_addr, orders_description, orders_item_count, orders_name, orders_status, orders_total_price) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into orders (orders_account_id, orders_addr, orders_description, orders_item_count, orders_name, orders_status, orders_total_price) values (?, ?, ?, ?, ?, ?, ?) 2020-01-04 15:43:33.610 INFO 8228 --- [ main] com.rtxtitanv.JpaTest : 保存结束
③查询测试
/** * 多表关联一对多查询 */ //解决延迟加载时Session已关闭出现的LazyInitializationException @Transactional @Rollback(false) @Test public void oneToManyFindTest() { //查询一个账户,并获取账户的所有订单 Optional<Account> accountOptional = accountRepository.findById(1L); if (!accountOptional.isPresent()) { logger.info("账户不存在"); } else { Account account = accountOptional.get(); logger.info("----------------账户信息----------------"); String accountInf = account.toString(); logger.info(accountInf); logger.info("----------------订单信息----------------"); account.getOrders().forEach(order -> logger.info(order.toString())); } //查询一个订单,并获取订单所对应的账户 Optional<Order> orderOptional = orderRepository.findById(1L); if (!orderOptional.isPresent()) { logger.info("订单不存在"); } else { Order order = orderOptional.get(); logger.info("----------------订单信息----------------"); String orderInfo = order.toString(); logger.info(orderInfo); logger.info("----------------账户信息----------------"); String accountInfo = order.getAccount().toString(); logger.info(accountInfo); } }
测试方法没加
@Transactional时查询时报了如下所示的LazyInitializationException,由于设置了延迟加载,通过get获取关联信息时,Session已关闭。解决方法如下:
- 不使用延迟加载,设置
fetch = FetchType.EAGER
,不推荐,此处应使用延迟加载提高性能。 - 查询方法上添加声明式事务
@Transactional
,推荐。 - 配置文件中添加
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
。
Hibernate: select account0_.account_id as account_1_0_0_, account0_.account_addr as account_2_0_0_, account0_.account_alias as account_3_0_0_, account0_.account_location as account_4_0_0_, account0_.account_name as account_5_0_0_, account0_.account_password as account_6_0_0_, account0_.account_rank as account_7_0_0_, account0_.account_tel as account_8_0_0_ from account account0_ where account0_.account_id=? 2020-01-05 14:55:03.709 INFO 8444 --- [ main] com.rtxtitanv.JpaTest : ----------------账户信息---------------- 2020-01-05 14:55:03.709 INFO 8444 --- [ main] com.rtxtitanv.JpaTest : Account{accountId=1, accountName='rtx_titan_v', accountPassword='123456', accountAlias='cdf_dv', accountAddr='重庆江北区', accountTel='13313313311', accountRank=8, accountLocation='china'} 2020-01-05 14:55:03.709 INFO 8444 --- [ main] com.rtxtitanv.JpaTest : ----------------订单信息---------------- org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rtxtitanv.model.Account.orders, could not initialize proxy - no Session
控制台打印的日志及自动生成的sql语句如下:
Hibernate: select account0_.account_id as account_1_0_0_, account0_.account_addr as account_2_0_0_, account0_.account_alias as account_3_0_0_, account0_.account_location as account_4_0_0_, account0_.account_name as account_5_0_0_, account0_.account_password as account_6_0_0_, account0_.account_rank as account_7_0_0_, account0_.account_tel as account_8_0_0_ from account account0_ where account0_.account_id=? 2020-01-05 16:36:49.349 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : ----------------账户信息---------------- 2020-01-05 16:36:49.349 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Account{accountId=1, accountName='rtx_titan_v', accountPassword='123456', accountAlias='cdf_dv', accountAddr='北京西城区', accountTel='13313313311', accountRank=7, accountLocation='china'} 2020-01-05 16:36:49.349 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : ----------------订单信息---------------- Hibernate: select orders0_.orders_account_id as orders_a8_2_0_, orders0_.orders_id as orders_i1_2_0_, orders0_.orders_id as orders_i1_2_1_, orders0_.orders_account_id as orders_a8_2_1_, orders0_.orders_addr as orders_a2_2_1_, orders0_.orders_description as orders_d3_2_1_, orders0_.orders_item_count as orders_i4_2_1_, orders0_.orders_name as orders_n5_2_1_, orders0_.orders_status as orders_s6_2_1_, orders0_.orders_total_price as orders_t7_2_1_ from orders orders0_ where orders0_.orders_account_id=? 2020-01-05 16:36:49.354 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Order{orderId=1, orderName='XBox Edit 2代', orderDescription='微软精英2代,无线蓝牙手柄国行', orderStatus='卖家已发货', orderTotalPrice='1390', orderItemCount=1, orderAddr='北京西城区'} 2020-01-05 16:36:49.354 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Order{orderId=3, orderName='XBox Edit 3代', orderDescription='微软精英3代,无线蓝牙手柄国行', orderStatus='卖家已发货', orderTotalPrice='1390', orderItemCount=1, orderAddr='北京西城区'} 2020-01-05 16:36:49.354 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Order{orderId=2, orderName='索尼ps5次世代游戏主机', orderDescription='索尼ps5,无索不玩', orderStatus='等待卖家发货', orderTotalPrice='5000', orderItemCount=1, orderAddr='北京西城区'} 2020-01-05 16:36:49.355 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : ----------------订单信息---------------- 2020-01-05 16:36:49.355 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Order{orderId=1, orderName='XBox Edit 2代', orderDescription='微软精英2代,无线蓝牙手柄国行', orderStatus='卖家已发货', orderTotalPrice='1390', orderItemCount=1, orderAddr='北京西城区'} 2020-01-05 16:36:49.355 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : ----------------账户信息---------------- 2020-01-05 16:36:49.355 INFO 14948 --- [ main] com.rtxtitanv.JpaTest : Account{accountId=1, accountName='rtx_titan_v', accountPassword='123456', accountAlias='cdf_dv', accountAddr='北京西城区', accountTel='13313313311', accountRank=7, accountLocation='china'}
④修改测试
/** * 多表关联一对多更新 */ @Transactional @Rollback(false) @Test public void oneToManyUpdateTest() { //通过一方更新多方的记录 Optional<Account> accountOpt = accountRepository.findById(1L); if (!accountOpt.isPresent()) { logger.info("账号不存在"); } else { Account account = accountOpt.get(); account.getOrders().forEach(order -> { if (order.getOrderId().equals(1L)) { order.setOrderTotalPrice("1590"); order.setOrderAddr("重庆渝北区"); } }); } //通过多方更新一方记录 Optional<Order> orderOpt = orderRepository.findById(1L); if (!orderOpt.isPresent()) { logger.info("订单不存在"); } else { Order order = orderOpt.get(); order.getAccount().setAccountAddr("重庆江北区"); order.getAccount().setAccountRank(8L); } }
后台执行时自动生成的sql语句如下,navicat刷新表查看更新成功。
Hibernate: select account0_.account_id as account_1_0_0_, account0_.account_addr as account_2_0_0_, account0_.account_alias as account_3_0_0_, account0_.account_location as account_4_0_0_, account0_.account_name as account_5_0_0_, account0_.account_password as account_6_0_0_, account0_.account_rank as account_7_0_0_, account0_.account_tel as account_8_0_0_ from account account0_ where account0_.account_id=? Hibernate: select orders0_.orders_account_id as orders_a8_1_0_, orders0_.orders_id as orders_i1_1_0_, orders0_.orders_id as orders_i1_1_1_, orders0_.orders_account_id as orders_a8_1_1_, orders0_.orders_addr as orders_a2_1_1_, orders0_.orders_description as orders_d3_1_1_, orders0_.orders_item_count as orders_i4_1_1_, orders0_.orders_name as orders_n5_1_1_, orders0_.orders_status as orders_s6_1_1_, orders0_.orders_total_price as orders_t7_1_1_ from orders orders0_ where orders0_.orders_account_id=? Hibernate: update account set account_addr=?, account_alias=?, account_location=?, account_name=?, account_password=?, account_rank=?, account_tel=? where account_id=? Hibernate: update orders set orders_account_id=?, orders_addr=?, orders_description=?, orders_item_count=?, orders_name=?, orders_status=?, orders_total_price=? where orders_id=?
⑤删除测试
/** * 多表关联一对多删除 */ @Transactional @Rollback(false) @Test public void oneToManyDeleteTest() { //account设置了级联删除,有多方关联时删除account将会同时删除关联的所有order //如果account没有设置级联删除,有多方关联删除一方时,默认置外键为null,如果外键不允许 //为空,会报错,如果配置了放弃维护关联关系则不能删除 //accountRepository.deleteById(1L); //只删除多方记录,直接删除会有问题,删除后再关联查询多方记录时没有生成删除语句 //orderRepository.deleteById(3L); Optional<Account> accountOptional = accountRepository.findById(1L); if (!accountOptional.isPresent()) { logger.info("账号不存在"); } else { Account account = accountOptional.get(); Set<Order> orders = account.getOrders(); logger.info("删除开始"); for (Iterator<Order> iterator = orders.iterator(); iterator.hasNext(); ) { //由于orphanRemoval = true,在一方关联多方的集合中移除多方,将会在多方删除这些记录 if (iterator.next().getOrderId().equals(1L)) { iterator.remove(); } } accountRepository.save(account); logger.info("删除结束"); } }
删除account表记录时同时删除orders 表关联的记录的sql语句如下,navicat查看删除成功。
Hibernate: select account0_.account_id as account_1_0_0_, account0_.account_addr as account_2_0_0_, account0_.account_alias as account_3_0_0_, account0_.account_location as account_4_0_0_, account0_.account_name as account_5_0_0_, account0_.account_password as account_6_0_0_, account0_.account_rank as account_7_0_0_, account0_.account_tel as account_8_0_0_ from account account0_ where account0_.account_id=? Hibernate: select orders0_.orders_account_id as orders_a8_1_0_, orders0_.orders_id as orders_i1_1_0_, orders0_.orders_id as orders_i1_1_1_, orders0_.orders_account_id as orders_a8_1_1_, orders0_.orders_addr as orders_a2_1_1_, orders0_.orders_description as orders_d3_1_1_, orders0_.orders_item_count as orders_i4_1_1_, orders0_.orders_name as orders_n5_1_1_, orders0_.orders_status as orders_s6_1_1_, orders0_.orders_total_price as orders_t7_1_1_ from orders orders0_ where orders0_.orders_account_id=? Hibernate: delete from orders where orders_id=? Hibernate: delete from orders where orders_id=? Hibernate: delete from orders where orders_id=? Hibernate: delete from account where account_id=?
在一方关联多方的集合中移除多方自动生成的删除多方记录的语句如下,navicat查看删除成功。
2020-01-04 18:37:36.308 INFO 4892 --- [ main] com.rtxtitanv.JpaTest : 删除开始 Hibernate: select orders0_.orders_account_id as orders_a8_1_0_, orders0_.orders_id as orders_i1_1_0_, orders0_.orders_id as orders_i1_1_1_, orders0_.orders_account_id as orders_a8_1_1_, orders0_.orders_addr as orders_a2_1_1_, orders0_.orders_description as orders_d3_1_1_, orders0_.orders_item_count as orders_i4_1_1_, orders0_.orders_name as orders_n5_1_1_, orders0_.orders_status as orders_s6_1_1_, orders0_.orders_total_price as orders_t7_1_1_ from orders orders0_ where orders0_.orders_account_id=? 2020-01-04 18:37:36.322 INFO 4892 --- [ main] com.rtxtitanv.JpaTest : 删除结束 Hibernate: delete from orders where orders_id=?
(2)多对多关联
①创建实体类并配置映射
package com.rtxtitanv.model; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.HashSet; import java.util.Set; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.Role * @description 角色实体类 * @date 2020/1/3 20:29 */ @Setter @Getter @Entity @Table(name = "role") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "role_id") private Long roleId; @Column(name = "role_name") private String roleName; @Column(name = "role_type") private String roleType; @ManyToMany(mappedBy = "roles", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY) private Set<Menu> menus = new HashSet<>(); @Override public String toString() { return "Role{" + "roleId=" + roleId + ", roleName='" + roleName + '\'' + ", roleType='" + roleType + '\'' + '}'; } }
package com.rtxtitanv.model; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.HashSet; import java.util.Set; /** * @author rtxtitanv * @version v1.0.0 * @name com.rtxtitanv.model.Menu * @description 菜单实体类 * @date 2020/1/3 20:30 */ @Setter @Getter @Entity @Table(name = "menu") public class Menu { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "menu_id") private Long menuId; @Column(name = "menu_name") private String menuName; @Column(name = "menu_path") private String menuPath; @Column(name = "menu_hidden") private Boolean menuHidden; @Column(name = "menu_parent_id") private Long menuParentId; @Column(name = "menu_icon") private String menuIcon; @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY) //配置中间表,joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段 //inverseJoinColumns:中间表的外键字段关联对方表的主键字段 @JoinTable(name = "role_menu", joinColumns = @JoinColumn(name = "menu_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles = new HashSet<>(); @Override public String toString() { return "Menu{" + "menuId=" + menuId + ", menuName='" + menuName + '\'' + ", menuPath='" + menuPath + '\'' + ", menuHidden=" + menuHidden + ", menuParentId=" + menuParentId + ", menuIcon='" + menuIcon + '\'' + '}'; } }
②新增测试
新建RoleRepository和MenuRepository并在测试类注入。
/** * 多表关联多对多保存 */ @Transactional @Rollback(false) @Test public void manyToManySaveTest() { Role role1 = new Role(); Role role2 = new Role(); Role role3 = new Role(); role1.setRoleName("admin"); role1.setRoleType("A"); role2.setRoleName("user"); role2.setRoleType("U"); role3.setRoleName("system"); role3.setRoleType("S"); Menu menu1 = new Menu(); Menu menu2 = new Menu(); Menu menu3 = new Menu(); menu1.setMenuName("导航栏"); menu1.setMenuHidden(false); menu1.setMenuPath("http://ascd/sddf/kk.img"); menu2.setMenuName("下拉框"); menu2.setMenuParentId(1L); menu2.setMenuIcon("下拉图标"); menu3.setMenuName("确认按钮"); menu3.setMenuIcon("方块"); menu3.setMenuPath("http://123.123.44.56/xx"); //关联操作 role1.getMenus().add(menu1); role1.getMenus().add(menu3); role2.getMenus().add(menu2); role2.getMenus().add(menu3); role3.getMenus().add(menu1); role3.getMenus().add(menu2); menu1.getRoles().add(role1); menu1.getRoles().add(role3); menu2.getRoles().add(role2); menu2.getRoles().add(role3); menu3.getRoles().add(role1); menu3.getRoles().add(role2); //保存,role和menu均设置了级联保存 menuRepository.save(menu1); //roleRepository.save(role1); 和save menu一样 }
自动生成的sql语句如下,navicat查看保存成功。
Hibernate: insert into menu (menu_hidden, menu_icon, menu_name, menu_parent_id, menu_path) values (?, ?, ?, ?, ?) Hibernate: insert into role (role_name, role_type) values (?, ?) Hibernate: insert into menu (menu_hidden, menu_icon, menu_name, menu_parent_id, menu_path) values (?, ?, ?, ?, ?) Hibernate: insert into role (role_name, role_type) values (?, ?) Hibernate: insert into menu (menu_hidden, menu_icon, menu_name, menu_parent_id, menu_path) values (?, ?, ?, ?, ?) Hibernate: insert into role (role_name, role_type) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?) Hibernate: insert into role_menu (menu_id, role_id) values (?, ?)
③查询测试
/** * 多表关联多对多查询 */ @Transactional @Rollback(false) @Test public void manyToManyFindTest() { //查询一个角色,并获取角色的所有菜单 Optional<Role> roleOptional = roleRepository.findById(1L); if (!roleOptional.isPresent()) { logger.info("角色不存在"); } else { Role role = roleOptional.get(); logger.info("----------------角色信息----------------"); String roleInf = role.toString(); logger.info(roleInf); logger.info("----------------菜单信息----------------"); role.getMenus().forEach(menu -> logger.info(menu.toString())); } //查询一个菜单,并获取菜单的所有角色 Optional<Menu> menuOptional = menuRepository.findById(1L); if (!menuOptional.isPresent()) { logger.info("菜单不存在"); } else { Menu menu = menuOptional.get(); logger.info("----------------菜单信息----------------"); String menuInfo = menu.toString(); logger.info(menuInfo); logger.info("----------------角色信息----------------"); menu.getRoles().forEach(role -> logger.info(role.toString())); } }
控制台打印的日志及自动生成的sql语句如下:
Hibernate: select role0_.role_id as role_id1_3_0_, role0_.role_name as role_nam2_3_0_, role0_.role_type as role_typ3_3_0_ from role role0_ where role0_.role_id=? 2020-01-05 17:48:38.412 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : ----------------角色信息---------------- 2020-01-05 17:48:38.412 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Role{roleId=1, roleName='system', roleType='S'} 2020-01-05 17:48:38.412 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : ----------------菜单信息---------------- Hibernate: select menus0_.role_id as role_id2_4_0_, menus0_.menu_id as menu_id1_4_0_, menu1_.menu_id as menu_id1_1_1_, menu1_.menu_hidden as menu_hid2_1_1_, menu1_.menu_icon as menu_ico3_1_1_, menu1_.menu_name as menu_nam4_1_1_, menu1_.menu_parent_id as menu_par5_1_1_, menu1_.menu_path as menu_pat6_1_1_ from role_menu menus0_ inner join menu menu1_ on menus0_.menu_id=menu1_.menu_id where menus0_.role_id=? 2020-01-05 17:48:38.420 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Menu{menuId=1, menuName='导航栏', menuPath='http://ascd/sddf/kk.img', menuHidden=false, menuParentId=null, menuIcon='null'} 2020-01-05 17:48:38.420 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Menu{menuId=2, menuName='下拉框', menuPath='null', menuHidden=null, menuParentId=1, menuIcon='下拉图标'} 2020-01-05 17:48:38.420 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : ----------------菜单信息---------------- 2020-01-05 17:48:38.420 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Menu{menuId=1, menuName='导航栏', menuPath='http://ascd/sddf/kk.img', menuHidden=false, menuParentId=null, menuIcon='null'} 2020-01-05 17:48:38.420 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : ----------------角色信息---------------- Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? 2020-01-05 17:48:38.422 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Role{roleId=1, roleName='system', roleType='S'} 2020-01-05 17:48:38.422 INFO 20880 --- [ main] com.rtxtitanv.JpaTest : Role{roleId=3, roleName='admin', roleType='A'}
④修改测试
/** * 多表关联多对多更新 */ @Transactional @Rollback(false) @Test public void manyToManyUpdateTest() { Optional<Role> roleOptional = roleRepository.findById(1L); if (!roleOptional.isPresent()) { logger.info("角色不存在"); } else { Role roles = roleOptional.get(); //通过id为1的role修改role关联的id为1的menu //根据id为1的menu修改menu关联的id为3的role roles.getMenus().forEach(menu -> { if (menu.getMenuId().equals(1L)) { menu.setMenuHidden(true); menu.setMenuName("左侧导航栏"); menu.getRoles().forEach(role -> { if (role.getRoleId().equals(3L)) { role.setRoleName("vip"); role.setRoleType("V"); } }); } }); } }
自动生成的sql语句如下,navicat刷新表查看更新成功。
Hibernate: select role0_.role_id as role_id1_3_0_, role0_.role_name as role_nam2_3_0_, role0_.role_type as role_typ3_3_0_ from role role0_ where role0_.role_id=? Hibernate: select menus0_.role_id as role_id2_4_0_, menus0_.menu_id as menu_id1_4_0_, menu1_.menu_id as menu_id1_1_1_, menu1_.menu_hidden as menu_hid2_1_1_, menu1_.menu_icon as menu_ico3_1_1_, menu1_.menu_name as menu_nam4_1_1_, menu1_.menu_parent_id as menu_par5_1_1_, menu1_.menu_path as menu_pat6_1_1_ from role_menu menus0_ inner join menu menu1_ on menus0_.menu_id=menu1_.menu_id where menus0_.role_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: update menu set menu_hidden=?, menu_icon=?, menu_name=?, menu_parent_id=?, menu_path=? where menu_id=? Hibernate: update role set role_name=?, role_type=? where role_id=?
⑤删除测试
/** * 多表关联多对多删除 */ @Transactional @Rollback(false) @Test public void manyToManyDeleteTest() { //准备删除的role Optional<Role> roleOptional = roleRepository.findById(1L); if (!roleOptional.isPresent()) { logger.info("角色不存在"); } else { Role role1 = roleOptional.get(); //关联关系解除(id为1的role-->id为1的menu,id为2的menu) //删除中间表的关联记录 role1.getMenus().forEach(menu -> { menu.getRoles().remove(role1); }); //不删除role的情况,重新关联(id为1的role-->id为3的menu) /*Optional<Menu> menuOptional = menuRepository.findById(3L); if (!menuOptional.isPresent()) { logger.info("菜单不存在"); } else { Menu menu3 = menuOptional.get(); role1.getMenus().add(menu3); menu3.getRoles().add(role1); //更新关联,可以省略,省略也会更新中间表关联关系 menuRepository.save(menu3); }*/ //删除role roleRepository.delete(role1); } }
解除并删除中间表关联后重新关联,执行时自动生成的sql语句如下,navicat查看中间表维护的关系更新成功。
Hibernate: select role0_.role_id as role_id1_3_0_, role0_.role_name as role_nam2_3_0_, role0_.role_type as role_typ3_3_0_ from role role0_ where role0_.role_id=? Hibernate: select menus0_.role_id as role_id2_4_0_, menus0_.menu_id as menu_id1_4_0_, menu1_.menu_id as menu_id1_1_1_, menu1_.menu_hidden as menu_hid2_1_1_, menu1_.menu_icon as menu_ico3_1_1_, menu1_.menu_name as menu_nam4_1_1_, menu1_.menu_parent_id as menu_par5_1_1_, menu1_.menu_path as menu_pat6_1_1_ from role_menu menus0_ inner join menu menu1_ on menus0_.menu_id=menu1_.menu_id where menus0_.role_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: select menu0_.menu_id as menu_id1_1_0_, menu0_.menu_hidden as menu_hid2_1_0_, menu0_.menu_icon as menu_ico3_1_0_, menu0_.menu_name as menu_nam4_1_0_, menu0_.menu_parent_id as menu_par5_1_0_, menu0_.menu_path as menu_pat6_1_0_ from menu menu0_ where menu0_.menu_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: delete from role_menu where menu_id=? and role_id=? Hibernate: delete from role_menu where menu_id=? and role_id=? Hibernate: insert into role_menu (menu_id, role_id) values (?, ?)
解除并删除中间表关联后删除,执行时自动生成的sql语句如下,navicat查看删除成功。
Hibernate: select role0_.role_id as role_id1_3_0_, role0_.role_name as role_nam2_3_0_, role0_.role_type as role_typ3_3_0_ from role role0_ where role0_.role_id=? Hibernate: select menus0_.role_id as role_id2_4_0_, menus0_.menu_id as menu_id1_4_0_, menu1_.menu_id as menu_id1_1_1_, menu1_.menu_hidden as menu_hid2_1_1_, menu1_.menu_icon as menu_ico3_1_1_, menu1_.menu_name as menu_nam4_1_1_, menu1_.menu_parent_id as menu_par5_1_1_, menu1_.menu_path as menu_pat6_1_1_ from role_menu menus0_ inner join menu menu1_ on menus0_.menu_id=menu1_.menu_id where menus0_.role_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: select roles0_.menu_id as menu_id1_4_0_, roles0_.role_id as role_id2_4_0_, role1_.role_id as role_id1_3_1_, role1_.role_name as role_nam2_3_1_, role1_.role_type as role_typ3_3_1_ from role_menu roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.menu_id=? Hibernate: delete from role_menu where menu_id=? and role_id=? Hibernate: delete from role_menu where menu_id=? and role_id=? Hibernate: delete from role where role_id=?
代码示例
- SpringBoot2.x 集成 SpringDataJpa 基于Kotlin
- SpringBoot2.x 集成 SpringDataJpa
- SpringBoot2.x 集成 SpringDataJpa 基于Kotlin
- Spring Boot 2.x基础教程:使用Spring Data JPA访问MySQL
- 从零开始学spring-boot(2)-集成spring-data-jpa
- spring-boot学习笔记(二)spring-data-jpa的简介以及项目集成
- SpringBoot系列之Spring Data Jpa集成教程
- springboot2.x整合spring-data-jpa的问题
- Springboot2.x新版整合SpringDataJpa中Hibernate默认创建的mysql表为myisam引擎修改为InnoDB问题
- SpringBoot | 第三十章:Spring-data-jpa的集成和使用
- SpringData JPA,Hibernate 与Spring Boot 集成
- spring boot集成data-jpa
- SpringBoot2.X集成JPA踩坑
- SpringBoot学习-(4)集成SpringDataJpa
- 初学SpringBoot2.x整合Spring Data JPA的坑。
- Spring Boot 系列(九)数据层-集成Spring-data-jpa
- springboot集成spring-data-jpa
- Spring Boot 系列(九)数据层-集成Spring-data-jpa
- spring boot 集成spring data jpa 时报错java.lang.ClassNotFoundException: org.hibernate.OptimisticLockExcep
- 从零开始学spring-boot(2)-集成spring-data-jpa