我们搭建一个maven项目,然后我们加载数据,数据我已经准备好了。
-- truncate all
delete from jpa_test.oo_t_idcard where 1=1;
delete from jpa_test.oo_t_person where 1=1;
-- oo_t_person
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (1, 4, '2019-12-12 02:55:14.786000000', 4, '2019-12-12 02:55:14.786000000', 'green', 62.00, 75.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (2, 4, '2019-12-12 02:55:14.841000000', 4, '2019-12-12 02:55:14.841000000', 'blue', 97.00, 23.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (3, 4, '2019-12-12 02:55:14.843000000', 4, '2019-12-12 02:55:14.843000000', 'yellow', 20.00, 9.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (4, 4, '2019-12-12 02:55:14.845000000', 4, '2019-12-12 02:55:14.845000000', 'blue', 102.00, 35.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (5, 4, '2019-12-12 02:55:14.847000000', 4, '2019-12-12 02:55:14.847000000', 'blue', 120.00, 45.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (6, 4, '2019-12-12 02:55:14.848000000', 4, '2019-12-12 02:55:14.848000000', 'blue', 98.00, 42.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (7, 4, '2019-12-12 02:55:14.851000000', 4, '2019-12-12 02:55:14.851000000', 'green', 156.00, 17.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (8, 4, '2019-12-12 02:55:14.854000000', 4, '2019-12-12 02:55:14.854000000', 'black', 72.00, 8.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (9, 4, '2019-12-12 02:55:14.856000000', 4, '2019-12-12 02:55:14.856000000', 'black', 105.00, 5.00);
INSERT INTO jpa_test.oo_t_person (id, created_by, created_date, last_modified_by, last_modified_date, hair_color, height, weight) VALUES (10, 4, '2019-12-12 02:55:14.858000000', 4, '2019-12-12 02:55:14.858000000', 'red', 172.00, 16.00);
-- oo_t_idcard
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (11, 4, '2019-12-12 02:55:15.716000000', 4, '2019-12-12 02:55:15.716000000', '6c398cd7-8ed9-4c6c-8080-120a1a826f89', '张三1', 0, 2);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (12, 4, '2019-12-12 02:55:15.719000000', 4, '2019-12-12 02:55:15.719000000', '87b5feff-8255-4ac6-aa69-6a9990048dcd', '孙七', 0, 3);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (13, 4, '2019-12-12 02:55:15.720000000', 4, '2019-12-12 02:55:15.720000000', '307dc084-a241-452b-b609-c824eb96bc44', '李四', 0, 4);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (14, 4, '2019-12-12 02:55:15.722000000', 4, '2019-12-12 02:55:15.722000000', '5e704f89-2d13-4794-9494-29dfa7c446ec', '赵六', 0, 5);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (15, 4, '2019-12-12 02:55:15.724000000', 4, '2019-12-12 02:55:15.724000000', 'b0ade2b0-a350-4536-90a0-4b09736493ec', '李四', 1, 6);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (16, 4, '2019-12-12 02:55:15.727000000', 4, '2019-12-12 02:55:15.727000000', 'a9c1531e-8719-4912-8bc7-ba3f7a5da05f', '李四', 1, 7);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (17, 4, '2019-12-12 02:55:15.729000000', 4, '2019-12-12 02:55:15.729000000', '1ad27d7b-0208-4adb-a856-d6a87dc4a227', '钱八', 0, 8);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (18, 4, '2019-12-12 02:55:15.733000000', 4, '2019-12-12 02:55:15.733000000', '08fd002e-bce7-4931-acb0-b18cf134880b', '王五', 1, 9);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (19, 4, '2019-12-12 02:55:15.740000000', 4, '2019-12-12 02:55:15.740000000', '34f09eef-c0f5-4418-936c-b58c2a82b256', '钱八', 1, 10);
INSERT INTO jpa_test.oo_t_idcard (id, created_by, created_date, last_modified_by, last_modified_date, card_no, name, sex, person_id) VALUES (20, 4, '2019-12-12 02:55:16.213000000', 4, '2019-12-12 02:55:16.213000000', 'a9963fd3-be45-483f-b5e1-e409de9c9c84', '赵六', 0, 1);
然后我们编写一个人和身份证两个实体类之间的一对一的关系:
@Entity
@Table(name = "oo_t_idcard")
@Data
@ToString(exclude = "person", callSuper = true)
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class IdCard extends DefaultEntity {
private static final long serialVersionUID = -4315695680043696544L;
/**
* 姓名
*/
private String name;
/**
* 身份证号
*/
private String cardNo;
/**
* 性别
*/
private Integer sex;
/**
* 外键
*/
@OneToOne
@JoinColumn(foreignKey = @ForeignKey(name = "fk_person"))
private Person person;
}
@Entity
@Table(name = "oo_t_person")
@Setter
@Getter
@ToString(exclude = "idCard", callSuper = true)
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Person extends DefaultEntity {
private static final long serialVersionUID = 4051419685118041233L;
/**
* 身高
*/
private BigDecimal height;
/**
* 体重
*/
private BigDecimal weight;
/**
* 头发颜色
*/
private String hairColor;
@OneToOne(mappedBy = "person", cascade = {CascadeType.MERGE, CascadeType.REMOVE})
private IdCard idCard;
}
然后就是定义接口继承hibernate提供的仓库JpaRepository如下代码:
/**
* 身份证 .
*
* @className: IDCardRepository
* @author: ws
* @date: 19-11-25 下午2:18
* @version: 1.0.0
*/
public interface IDCardRepository extends JpaRepository<IdCard, Long> {
}
/**
* 人 数据访问层 .
*
* @className: PersonRepository
* @author: ws
* @date: 19-11-25 下午2:12
* @version: 1.0.0
*/
public interface PersonRepository extends BaseRepository<Person, Long> {
Optional<Person> findByWeightAndHeight(BigDecimal weight, BigDecimal height);
int countByHairColor(String hairColor);
@Query("from Person p left join fetch p.idCard i where i.name = ?1")
Person findSomePersonByNameAtJPQL(String name);
@Query(value = "select * from oo_t_person p left join oo_t_idcard oti on p.id = oti.person_id where oti.name = ?1"
, nativeQuery = true)
Person findSomePersonByNameAtNative(String name);
@Transactional
long deleteByHeight(BigDecimal height);
}
最后我们边测试边理解hibernate的各种api方法,和查询方法和规则:
public class One2OneRepositoryTest extends BaseTest {
/**
* 测试数据数量
*/
private int dataCount = 10;
/**
* 姓名数组
*/
private String[] nameArr = {"张三", "李四", "王五", "赵六", "孙七", "钱八"};
/**
* 性别数组
*/
private Integer[] sexArr = {0, 1};
private String[] hairColorArr = {"red", "green", "black", "blue", "yellow", "pink", "gray"};
/**
* 随机数
*/
private Random random = new Random();
@Autowired
private DynamicLeftJoinQuery dynamicLeftJoinQuery;
@Autowired
private EntityManager entityManager;
@Test
@Order(1)
@Disabled
@DisplayName("批量数据保存(Person -> IdCard)")
void testSave1() {
// Person表保存所有生成出来的数据
System.out.println("=== Person表保存所有生成出来的数据 =========================================");
Collection<Person> persons = initPersonData(dataCount);
personRepository.saveAll(persons);
// 将第一个Person数据从persons集合移出,并将其他Person数据生成对应的idCard数据
Person firstPerson = persons.iterator().next();
persons.remove(firstPerson);
System.out.println("=== 将第一个Person数据从persons集合移出,并将其他Person数据生成对应的idCard数据 =========================================");
Collection<IdCard> idCards = initIDCardData(persons);
// 保存idCards集合的所有数据,除第一个Person
System.out.println("=== 保存idCards集合的所有数据,除第一个Person =========================================");
idCardRepository.saveAll(idCards);
// 用自由态的方式关联赋值给idCard,并保存
System.out.println("=== 用自由态的方式关联赋值给idCard,并保存 =========================================");
idCards = initIDCardData(Collections.singleton((Person) new Person().setId(firstPerson.getId())));
idCardRepository.saveAll(idCards);
assertAll(
// 随机检查fk有没有生成
() -> assertThat(assertThat(idCardRepository.getOne()
.getPerson()
.getId())
.isNotNull()),
// 检查第一个Person的fk关联有没有建立成功
() -> assertThat(assertThat(idCardRepository
.count(Example.of(new IdCard()
.setPerson((Person) new Person()
.setId(firstPerson.getId())))))
.isEqualTo(1))
);
}
@Test
@Order(2)
@Disabled
@DisplayName("批量数据保存(IdCard -> Person)")
void testSave2() {
Person person = initPersonData(1).get(0);
IdCard idCard = initIDCardData(Collections.singleton(person)).get(0);
idCard.setPerson(null);
// 先分别保存无关联的idCard和person
System.out.println("=== 先分别保存无关联的idCard和person =========================================");
idCardRepository.save(idCard);
personRepository.save(person);
// 检查这个idCard确无person关联
System.out.println("=== 检查这个idCard确无person关联 =========================================");
assertThat(idCardRepository.getOne(idCard.getId()).getPerson()).isNull();
// 关联赋值person后,再次保存
System.out.println("=== 关联赋值person后,再次保存 =========================================");
idCard.setPerson(person);
idCardRepository.save(idCard);
// 再次检查这个idCard应有person对象内容
System.out.println("=== 再次检查这个idCard应有person对象内容 =========================================");
// 关于fetch
// 注意:在IdCard里配置了fetch=lazy时,调用person#getHeight()时会触发1 + N查询。而fetch=eager时,会先join查询出来,这里能直接get到
assertThat(idCardRepository.getOne(idCard.getId()).getPerson().getHeight()).isNotNull();
}
/**
* 单表删除测试删除(IdCard)
* 注:依赖testSave1()
*/
@Test
@Order(3)
@Disabled
@DisplayName("单表删除(IdCard)")
void testDelete1() {
// 默认无任何Cascade.REMOVE配置时开始测试
// 只删除IdCard(从待久态里删除)
IdCard idCard = idCardRepository.getOne();
System.out.println("=== 只删除IdCard(从待久态里删除) =========================================");
idCardRepository.delete(idCard);
assertAll(
// 检查该idCard是否真删除
() -> assertThat(idCardRepository.findById(idCard.getId()).orElse(null)).isNull(),
// 检查idCard对应的person数据没受影响
() -> assertThat(personRepository.findById(idCard.getPerson().getId()).orElse(null)).isNotNull()
);
// // 只删除IdCard(从自由态里删除)
// IdCard temp = idCardRepository.getOne();
// IdCard nextIdCard = (IdCard) new IdCard().setId(temp.getId());
// System.out.println(nextIdCard.getId());
// System.out.println("=== 只删除IdCard(从自由态里删除) =========================================");
// idCardRepository.delete(nextIdCard);
//
// assertAll(
// // 检查该idCard是否真删除
// () -> assertThat(idCardRepository.findById(nextIdCard.getId()).orElse(null)).isNull(),
// // 检查idCard对应的person数据没受影响
// () -> assertThat(personRepository.findById(temp.getPerson().getId()).orElse(null)).isNotNull()
// );
}
/**
* 单表删除测试删除(IdCard)
* 结论:在有fk关联时不存在单删Person的情况。要么接受删除失败,要么修改设置成cascade=REMOVE
* 注:依赖testSave1()
*/
@Test
@Order(4)
@Disabled
@DisplayName("单表删除(Person)")
void testDelete2() {
// 默认无任何cascade配置时开始测试
// 只删除Person(从待久态里删除)
Person person = personRepository.getOne();
// 这里实验了delete()方法实为byId删除
// 如果故意将id赋null,会报"The given id must not be null!"异常
// person.setId(null);
personRepository.delete(person);
// 如果故意单传person#id来删除,会因idCard的id没找到,触发fk约束,致删除失败。
// 异常信息:Cannot delete or update a parent row: a foreign key constraint fails
// personRepository.delete((Person) new Person().setId(person.getId()));
// 所以,单传id删除的正确姿势如下
// personRepository.deleteById(person.getId());
// 或直接简单调用delete(Entity)
// personRepository.delete(person);
// 或将2个对象的id单独提供出来delete
// personRepository.delete((Person) new Person().setIdCard((IdCard) new IdCard().setId(person.getIdCard().getId())).setId(person.getId()));
// 以上3选一
assertAll("单表删除(Person)结果验证",
// 检查该person是否真删除
// 由于person有idCard表里有fk约束,所以,直接删除person会报"Cannot delete or update a parent row: a foreign key constraint fails"异常
// 有2个方法解决
// 1. 在IdCard#person上加@OnDelete(action=OnDeleteAction.CASCADE)。此时会在建fk时加上"ON DELETE CASCADE"级联关系
// 2. 在Person#idCard上加@OneToOne(cascade = CascadeType.REMOVE)
// 所以,在有fk关联时不存在单删Person的情况
() -> assertThat(personRepository.findById(person.getId()).orElse(null)).isNotNull(),
// 因此,此检查在这个case里为false
() -> assertThat(idCardRepository.findById(person.getIdCard().getId()).orElse(null)).isNotNull()
);
// // 只删除Person(从自由态里删除)
// Person nextPerson = (Person) new Person().setId(personRepository.getOne().getId());
// personRepository.delete(nextPerson);
//
// assertAll(
// // 检查该person是否真删除
// () -> assertThat(personRepository.findById(nextPerson.getId()).orElse(null)).isNull(),
// // 检查person对应的idCard数据没受影响
// () -> assertThat(idCardRepository.findOne(Example.of(new IdCard()
// .setPerson((Person) new Person()
// .setId(nextPerson.getId())))).orElse(null))
// .isNotNull()
// );
}
/**
* 级联更新(Person)
*
*/
@Test
@Order(5)
@Disabled
@DisplayName("级联更新(Person)")
void testCascadeUpdate1() {
Person person = personRepository.getOne();
person.setHairColor(hairColorArr[0]);
// 这里height=null后,会将该列数据update成null。即save()里的merge方法是全量更新
person.setHeight(null);
// Person#idCard上加cascade = CascadeType.MERGE后可达到级联更新idCard的效果
person.getIdCard().setName("Elsa");
System.out.println("=== 更新Person和级联更新 IdCard =========================================");
personRepository.save(person);
Person result = personRepository.getOne(person.getId());
// 结论:person能正常update自己和idCard上的属性
assertAll("级联更新结果验证",
() -> assertThat(result.getHairColor()).isEqualTo(hairColorArr[0]),
() -> assertThat(result.getIdCard().getName()).isEqualTo("Elsa")
);
person = personRepository.getOne(1);
// 故意将IdCard赋null
person.setIdCard(null);
person.setHairColor("yellow1");
System.out.println("=== 级联更新 IdCard =========================================");
personRepository.save(person);
// 检查在把Person#idCard赋null后,会不会在save()后删除该idCard?
// 结论:不会。实际上@OneToOne两边都不会
assertThat(idCardRepository.findOne(Example.of(new IdCard()
.setPerson((Person) new Person()
.setId(person.getId())))).orElse(null))
.isNotNull();
}
/**
* 级联更新(IdCard)
*
*/
@Test
@Order(6)
@Disabled
@DisplayName("级联更新(IdCard)")
void testCascadeUpdate2() {
System.out.println("IdCard是从表,不建议(或不允许)级联更新Person!");
}
@Test
@Order(7)
@Disabled
@DisplayName("JPA默认查询接口(by单Id)")
void testQuery1() {
Long personId = personRepository.getOne().getId();
// find系列方法强调“找”,所以,在有可能找不到的情况下用Optional来判断存在性
System.out.println("=== 可正常获取到person的find方法 ========================");
assertThat(personRepository.findById(personId).orElse(null)).isNotNull();
System.out.println("=== 不能正常获取到person的find方法 ========================");
assertThat(personRepository.findById(-1L).orElse(null)).isNull();
// get系列方法强调“取”,所以,在取不到的情况下会抛EntityNotFoundException异常
System.out.println("=== 可正常获取到person的get方法 ========================");
assertThat(personRepository.getOne(personId)).isNotNull();
System.out.println("=== 不能正常获取到person的get方法 ========================");
// 此时再判断isNull()已无意义,get不到会直接抛EntityNotFoundException异常
assertThrows(EntityNotFoundException.class, () -> personRepository.getOne(-1L));
}
@Test
@Order(8)
@Disabled
@DisplayName("JPA默认查询接口(by多Id)")
void testQuery2() {
int count = 3;
Iterable<Long> ids = personRepository.getMany(count)
.stream()
.map(Person::getId)
.collect(Collectors.toList());
// 注意:JPA的语法是方法名要对应field name。所以,不存在所谓的getByIds这样的写法,除非field name真是的ids
// “多”的定义体现在介词前面。例如:findAllById
System.out.println("=== 可正常获取到多个person的findAll方法 ========================");
assertThat(personRepository.findAllById(ids).size()).isEqualTo(count);
// 查不到也会返回一个empty list
assertThat(personRepository.findAllById(Arrays.asList(-1L, -2L, -3L))).isNotNull().isEmpty();
}
@Test
@Order(9)
@Disabled
@DisplayName("JPA默认查询接口(分页查询)")
void testQuery3() {
int pageNo = 0; // 从0开始
int pageSize = dataCount / 2;
Page<Person> page = personRepository.page(pageNo, pageSize);
System.out.println("=== 分页结果 ===========================");
System.out.println("=== page size: ===========================");
System.out.println(pageSize);
System.out.println("=== total rows: ===========================");
System.out.println(page.getTotalElements());
System.out.println("=== total page: ===========================");
System.out.println(page.getTotalPages());
System.out.println("=== contents: ===========================");
System.out.println(page.getContent());
}
@Test
@Order(10)
@Disabled
@DisplayName("JPA默认查询接口(分页 + 排序)")
void testQuery4() {
int pageNo = 0;
int pageSize = dataCount / 2;
// 这里的height是field name
// 多列排序用and()
// 升序用ascending()
// 降序用descending()
Sort sort = Sort.by("height").ascending();
// 另外,javax.persistence还提供了@OrderBy注解,可定义在field name上。每次查询都会带上。可酌情使用
Page<Person> page = personRepository.findAll(PageRequest.of(pageNo, pageSize));
Page<Person> pageAndSort = personRepository.findAll(PageRequest.of(pageNo, pageSize, sort));
System.out.println("=== 分页 + 排序结果 ===========================");
System.out.println("=== 未排序数据: ===========================");
System.out.println(page.stream().map(Person::getHeight).collect(Collectors.toList()));
System.out.println("=== 已排序数据: ===========================");
System.out.println(pageAndSort.stream().map(Person::getHeight).collect(Collectors.toList()));
}
@Test
@Order(11)
@Disabled
@DisplayName("JPA默认查询接口(Example)")
void testQuery5() {
// Example查询会把Entity的field names作为返回值。field非null值作为where条件
// 等同于原脚手架里提供的getByEntity(Entity entity)方法
// JPA的find系列用Example还可以追加Page和Sort
Person person = personRepository.getOne();
// byId查询的另一种写法
System.out.println("=== byId查询的另一种写法 ===========================");
assertThat(personRepository.findOne(Example.of((Person) new Person().setId(person.getId()))).orElse(null)).isNotNull();
// byHeight查询
System.out.println("=== byHeight查询 ===========================");
assertThat(personRepository.findAll(Example.of(new Person().setHeight(person.getHeight())))).isNotEmpty();
// JPA还提供了ExampleMatcher查询条件匹配器,用于和Example配合生成动态查询条件。如忽略大小写、模糊匹配等。
// 相关说明和使用可参考:
// Spring官方文档:5.6.3. Example Matchers
// https://blog.csdn.net/moshowgame/article/details/80282813
// https://www.cnblogs.com/powerwu/articles/10716070.html
// 示例略
}
@Test
@Order(12)
@Disabled
@DisplayName("JPA默认查询接口(Others)")
void testQuery6() {
// count查询
System.out.println("=== count查询 ===========================");
long count = personRepository.count(Example.of(new Person().setHairColor(hairColorArr[0])));
assertThat(count).isGreaterThan(0);
// existsByExample查询
System.out.println("=== existsByExample查询 ===========================");
boolean exists = personRepository.exists(Example.of(new Person().setHairColor(hairColorArr[0])));
assertThat(exists).isTrue();
// existsById查询
System.out.println("=== existsById查询 ===========================");
Person person = personRepository.getOne();
assertThat(personRepository.existsById(person.getId())).isTrue();
}
@Test
@Order(13)
// @Disabled
@DisplayName("用方法命名规则查询")
void testQuery7() {
// JPA还提供一种“Query Methods”的查询方式。即对method进行一套基于规则要求的命名后可直接用于查询。大量减少了简单查询的sql开发要求
// 命名规则说明请参见Spring官方文档
// 5.3 Query Methods
// Table 3. Supported keywords inside method names
// 另外,JPA除find前缀的查询方式,还支持delete(remove), count, exists前缀方式
// 注意:delete前缀需要再补充@Transactional
Person person = personRepository.getOne();
System.out.println("=== Query Methods命名规则find查询(可正常返回) ===========================");
assertThat(personRepository.findByWeightAndHeight(person.getWeight(), person.getHeight())
.orElse(null)).isNotNull();
// find查询支持对返回值的Optional、Stream、Future、等包装
System.out.println("=== Query Methods命名规则find查询(不可正常返回) ===========================");
assertThat(personRepository.findByWeightAndHeight(new BigDecimal(-1), new BigDecimal(-1))
.orElse(null)).isNull();
System.out.println("=== Query Methods命名规则count查询 ===========================");
assertThat(personRepository.countByHairColor(hairColorArr[0])).isNotNull();
}
@Test
@Order(14)
@Disabled
@DisplayName("方法级自定义JPQL")
void testQuery8() {
// 如mybatis一样,JPA也提供了方法上直接自定义JPQL的方式
// 即在方法定义上注明@Query
// JPQL=Java持久化查询语言
// 同时,@Query也提供对原始sql的支持。即设置nativeQuery=true
// @Query也支持对delete, count等语法支持
// 注意:在用delete时,还要加上@Modifying
System.out.println("=== 方法级自定义JPQL ===========================");
assertThat(personRepository.findSomePersonByNameAtJPQL(nameArr[0])).isNotNull();
System.out.println("=== 方法级自定义native sql ===========================");
assertThat(personRepository.findSomePersonByNameAtNative(nameArr[0])).isNotNull();
// 更多的JPQL语法及查询示例可参考:
// https://blog.csdn.net/czp11210/article/details/50799489
}
@Test
@Order(15)
@Disabled
@DisplayName("Wrapper式查询")
void testQuery9() {
// Hibernate提供能像mybatis-plus里Wrapper类似的用Java语句拼装sql的方式
// 核心对象:EntityManager
// 完成如下sql查询的写法:
// select count(*) from Person
System.out.println("=== countAllPerson查询 ===========================");
CriteriaBuilder builder1 = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery1 = builder1.createQuery(Long.class);
Root<Person> root1 = criteriaQuery1.from(Person.class);
criteriaQuery1.select(builder1.count(root1));
Long count = entityManager.createQuery(criteriaQuery1).getSingleResult();
assertThat(count).isEqualTo(dataCount);
System.out.println("===================================================");
// 完成如下sql查询的写法:
// select * from Person p where p.weight = ?
Person person = personRepository.getOne();
System.out.println("=== countAllPerson查询 ===========================");
CriteriaBuilder builder2 = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteriaQuery2 = builder2.createQuery(Person.class);
Root<Person> root2 = criteriaQuery2.from(Person.class);
criteriaQuery2.where(builder2.equal(root2.get("weight"), person.getWeight()));
assertThat(entityManager.createQuery(criteriaQuery2).getResultList()).isNotEmpty();
// 更多CriteriaBuilder的用法参考:
// Hibernate官方文档:16. Criteria
// 动态构造查询简单介绍:https://blog.csdn.net/zhaoruda/article/details/80157975
// 一个复杂查询的例子:https://www.cnblogs.com/g-smile/p/9177841.html
}
@Test
@Order(16)
@Disabled
@DisplayName("EntityManager手写JPQL")
void testQuery10() {
// 此方式也支持对原始SQL的手写。调用createNativeQuery()方法即可
TypedQuery<Person> query = entityManager.createQuery("from Person p where p.hairColor = ?1", Person.class);
query.setParameter(1, hairColorArr[0]);
assertThat(query.getResultList()).isNotEmpty();
}
@Test
@Order(17)
@Disabled
@DisplayName("动态JPQL")
public void testDynamicQuery() {
// Person person = personRepository
// .findAll(PageRequest.of(1, 1))
// .stream()
// .findAny()
// .orElse(new Person());
//
// System.out.println("Person: " + person);
//
// System.out.println(dynamicLeftJoinQuery.queryByEntity(new Person().setIdCard(new IdCard().setName(person.getIdCard().getName())).setWeight(person.getWeight())));
}
/**
* 初始化一定数量的 Person .
* @param count
* @author ws
* @date 19-11-25 下午3:11
* @return java.util.List<com.maxnerva.test.maxbase.common.jpa.one2one.entity.Person>
**/
private List<Person> initPersonData(int count) {
return Stream.iterate(0, i -> i + 1).limit(count)
.map(i -> new Person()
.setHeight(new BigDecimal(random.nextInt(200)))
.setWeight(new BigDecimal(random.nextInt(100)))
.setHairColor(hairColorArr[random.nextInt(hairColorArr.length)]))
.collect(Collectors.toList());
}
/**
* 初始化 身份证 数据 .
* @param persons
* @author ws
* @date 19-11-26 上午10:26
* @return java.util.List<com.maxnerva.test.maxbase.common.jpa.one2one.entity.IDCard>
**/
private List<IdCard> initIDCardData(Collection<Person> persons) {
return persons.stream()
.map(person -> {
IdCard idCard = new IdCard();
idCard.setName(nameArr[random.nextInt(nameArr.length)]);
idCard.setSex(sexArr[random.nextInt(sexArr.length)]);
idCard.setCardNo(StrUtil.uuid());
idCard.setPerson(person);
return idCard;
})
.collect(Collectors.toList());
}
@Autowired
private PersonRepository personRepository;
@Autowired
private IDCardRepository idCardRepository;
}
来源:CSDN
作者:蛋蛋淡淡定
链接:https://blog.csdn.net/qq_39361934/article/details/103695923