SpringDataJpa增删改查
技术背景:
开发工具:STS(eclipse)
技术选择: SpringBoot, SpringDataJpa,
数据库: MySQL, Redis
使用的是maven多模块开发,所以创建SpringBoot项目是先创建maven项目然后引入相应的依赖。(多模块项目创建参考: https://www.jianshu.com/p/ca3caa6614c1)
1:创建maven子项目study-springboot-domain引入一下依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>study-springboot</groupId>
<artifactId>study-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>study-springboot-domain</artifactId>
<description>实体</description>
<dependencies>
<!-- 公共项目 -->
<dependency>
<groupId>study-springboot</groupId>
<artifactId>study-springboot-comm</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 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>
<scope>runtime</scope>
</dependency>
<!-- 自动装配 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<!-- 打包 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
父级项目依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>study-springboot</groupId>
<artifactId>study-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<description>springboot学习demo</description>
<properties>
<!-- 字符编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- maven打包插件 -->
<maven-jar-plugin.version>2.6</maven-jar-plugin.version>
</properties>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试包,当我们使用 mvn package 的时候该包并不会被打入,因为它的生命周期只在 test 之内 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 简化简单实体getter,setter -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugin</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>study-springboot-domain</module>
<module>study-springboot-backstage</module>
<module>study-springboot-comm</module>
</modules>
</project>
公共项目依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>study-springboot</groupId>
<artifactId>study-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>study-springboot-comm</artifactId>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
parent:引入了spring-boot-starter-parent,以及测试和lombok项目。
comm: 引入了spring-boot-starter-web,和工具包
domain:主要就是数据库操作项目(集成SpringDataJpa和Redis)
domain配置文件:app
spring:
datasource:
# 数据库链接--不能使用 url: xxxxxxxxxxxxxxxxx
jdbc-url: jdbc:mysql:///db_comm_01?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: Root1234
driver-class-name: com.mysql.jdbc.Driver
member:
datasource:
jdbc-url: jdbc:mysql:///db_member_01?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: Root1234
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
show-sql: true
hibernate:
ddl-auto: none
redis:
database: 0 # database name[0-15]
host: localhost # server host
password: Redis1234! # server password
port: 6379 # connection port
pool:
max-idle: 8 # pool settings ...
min-idle: 0
max-active: 8
max-wait: -1
配置文件其实主要就是配置数据库信息的这里使用了多数据源
jpa.hibernate.ddl-auto=none;不自动执行数据库模式定义语言语句
jpa.show-sql=true; 打印SQL语句
集成JPA参考: https://www.jianshu.com/p/1186134d0395
在pom文件引入依赖其实redis已经自动集成好了
注入RedisTemplate 即可以操作;但是插入的数据和key前面出现特殊字符\xac\xed\x00\x05t\x00\(没有实际影响)
解决方式是自定义序列化方式;所以添加配置类RedisConfig
package study.springboot.domain.config.redis;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class RedisConfig extends CachingConfigurerSupport{
/**
* RedisTemplate配置
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
//定义key序列化方式
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//定义value的序列化方式
Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setKeySerializer(redisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
集成完毕;使用jpa逆向生成实体。
结构说明
测试Redis:
执行代码
执行结果
测试JPA操作(增删改-查)查是重点
表结构关系 user, role, user_role ; 多对多关系。
增:save
@Test
public void test() {
SysUser sysUser = new SysUser();
sysUser.setId(null);
sysUser.setName("bertram.wang");
sysUser.setPassword("1234567890");
List<SysRole> roles = new ArrayList<>();
SysRole role = new SysRole();
role.setId(1);
roles.add(role);
sysUser.setRoles(roles);
userRepository.save(sysUser);
}
执行的SQL
删除:
public void test() {
SysUser sysUser = new SysUser();
// 因为我数据库插入后ID=9
sysUser.setId(9);
userRepository.delete(sysUser);
}
执行的SQL
修改:(目标需要删除以前的关系,再添加现在的关系)
@Test
public void test() {
SysUser sysUser = new SysUser();
sysUser.setId(10);
sysUser.setName("bertram");
sysUser.setPassword("qwertyuiop");
List<SysRole> roles = new ArrayList<>();
SysRole role = new SysRole();
role.setId(2);
roles.add(role);
sysUser.setRoles(roles);
userRepository.save(sysUser);
}
执行的SQL
发现修改和新增都是用的save方法,只不过id有区别。
查询:
1:简单查询findOneById: 根据ID查询一条数据
@Test
public void findOneById() {
SysUser sysUser = userRepository.findOneById(2);
log.info("sysUser:{}",sysUser);
}
--执行的SQL:
Hibernate: select sysuser0_.id as id1_3_, sysuser0_.create_date as create_d2_3_, sysuser0_.modify_date as modify_d3_3_, sysuser0_.name as name4_3_, sysuser0_.password as password5_3_ from sys_user sysuser0_ where sysuser0_.id=?
Hibernate: select roles0_.user_id as user_id5_4_0_, roles0_.role_id as role_id4_4_0_, sysrole1_.id as id1_2_1_, sysrole1_.create_date as create_d2_2_1_, sysrole1_.modify_date as modify_d3_2_1_, sysrole1_.name as name4_2_1_, sysrole1_.parent_id as parent_i5_2_1_ from sys_user_role roles0_ inner join sys_role sysrole1_ on roles0_.role_id=sysrole1_.id where roles0_.user_id=?
自动关联查询了role信息;(实体配置如下)
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "sys_user_role",joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<SysRole> roles;
2:分页查询
@Test
public void findAllTest() {
PageRequest pageRequest = PageRequest.of(1, 1, Direction.DESC, "id");
SysUser sysUser = new SysUser();
sysUser.setName("admi");
Page<SysUser> page = userRepository.findAll(Example.of(sysUser,ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)), pageRequest);
List<SysUser> content = page.getContent();
log.info("content:{}", content);
}
Hibernate: select sysuser0_.id as id1_3_, sysuser0_.create_date as create_d2_3_, sysuser0_.modify_date as modify_d3_3_, sysuser0_.name as name4_3_, sysuser0_.password as password5_3_ from sys_user sysuser0_ where sysuser0_.name like ? order by sysuser0_.id desc limit ?, ?
Hibernate: select roles0_.user_id as user_id5_4_0_, roles0_.role_id as role_id4_4_0_, sysrole1_.id as id1_2_1_, sysrole1_.create_date as create_d2_2_1_, sysrole1_.modify_date as modify_d3_2_1_, sysrole1_.name as name4_2_1_, sysrole1_.parent_id as parent_i5_2_1_ from sys_user_role roles0_ inner join sys_role sysrole1_ on roles0_.role_id=sysrole1_.id where roles0_.user_id=?
Hibernate: select menus0_.role_id as role_id5_1_0_, menus0_.menu_id as menu_id4_1_0_, sysmenu1_.id as id1_0_1_, sysmenu1_.create_date as create_d2_0_1_, sysmenu1_.modify_date as modify_d3_0_1_, sysmenu1_.icon as icon4_0_1_, sysmenu1_.name as name5_0_1_, sysmenu1_.order_num as order_nu6_0_1_, sysmenu1_.parent_id as parent_i7_0_1_, sysmenu1_.permission as permissi8_0_1_, sysmenu1_.type as type9_0_1_, sysmenu1_.url as url10_0_1_ from sys_menu_role menus0_ inner join sys_menu sysmenu1_ on menus0_.menu_id=sysmenu1_.id where menus0_.role_id=?
Hibernate: select count(sysuser0_.id) as col_0_0_ from sys_user sysuser0_ where sysuser0_.name like ?
第一行SQL 语句-- xxxx where sysuser0_.name like ? order by sysuser0_.id desc limit ?, ?(模糊条件分页)
PageRequest源码
/*
* Copyright 2008-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.lang.Nullable;
/**
* Basic Java Bean implementation of {@code Pageable}.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class PageRequest extends AbstractPageRequest {
private static final long serialVersionUID = -4541509938956089562L;
private final Sort sort;
/**
* Creates a new {@link PageRequest}. Pages are zero indexed, thus providing 0 for {@code page} will return the first
* page.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @deprecated use {@link #of(int, int)} instead.
*/
@Deprecated
public PageRequest(int page, int size) {
this(page, size, Sort.unsorted());
}
/**
* Creates a new {@link PageRequest} with sort parameters applied.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @param direction the direction of the {@link Sort} to be specified, can be {@literal null}.
* @param properties the properties to sort by, must not be {@literal null} or empty.
* @deprecated use {@link #of(int, int, Direction, String...)} instead.
*/
@Deprecated
public PageRequest(int page, int size, Direction direction, String... properties) {
this(page, size, Sort.by(direction, properties));
}
/**
* Creates a new {@link PageRequest} with sort parameters applied.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @param sort can be {@literal null}.
* @deprecated since 2.0, use {@link #of(int, int, Sort)} instead.
*/
@Deprecated
public PageRequest(int page, int size, Sort sort) {
super(page, size);
this.sort = sort;
}
/**
* Creates a new unsorted {@link PageRequest}.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @since 2.0
*/
public static PageRequest of(int page, int size) {
return of(page, size, Sort.unsorted());
}
/**
* Creates a new {@link PageRequest} with sort parameters applied.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @param sort must not be {@literal null}.
* @since 2.0
*/
public static PageRequest of(int page, int size, Sort sort) {
return new PageRequest(page, size, sort);
}
/**
* Creates a new {@link PageRequest} with sort direction and properties applied.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @param direction must not be {@literal null}.
* @param properties must not be {@literal null}.
* @since 2.0
*/
public static PageRequest of(int page, int size, Direction direction, String... properties) {
return of(page, size, Sort.by(direction, properties));
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.Pageable#getSort()
*/
public Sort getSort() {
return sort;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.Pageable#next()
*/
public Pageable next() {
return new PageRequest(getPageNumber() + 1, getPageSize(), getSort());
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.AbstractPageRequest#previous()
*/
public PageRequest previous() {
return getPageNumber() == 0 ? this : new PageRequest(getPageNumber() - 1, getPageSize(), getSort());
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.Pageable#first()
*/
public Pageable first() {
return new PageRequest(0, getPageSize(), getSort());
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PageRequest)) {
return false;
}
PageRequest that = (PageRequest) obj;
return super.equals(that) && this.sort.equals(that.sort);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return 31 * super.hashCode() + sort.hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Page request [number: %d, size %d, sort: %s]", getPageNumber(), getPageSize(), sort);
}
}
不难看出这个方法
public static PageRequest of(int page, int size, Direction direction, String... properties) {
return of(page, size, Sort.by(direction, properties));
}
可以实现根据多个字段同向排序的功能。实际需求更多的可能是多个字段不通向的排序。立马看到方法:
/**
* Creates a new {@link PageRequest} with sort parameters applied.
*
* @param page zero-based page index.
* @param size the size of the page to be returned.
* @param sort must not be {@literal null}.
* @since 2.0
*/
public static PageRequest of(int page, int size, Sort sort) {
return new PageRequest(page, size, sort);
}
再查看Sort源码存在静态方法
public static Sort by(List<Order> orders) {
Assert.notNull(orders, "Orders must not be null!");
return orders.isEmpty() ? Sort.unsorted() : new Sort(orders);
}
一直跟进Order
public static class Order implements Serializable {
private static final long serialVersionUID = 1522511010900108987L;
private static final boolean DEFAULT_IGNORE_CASE = false;
private static final NullHandling DEFAULT_NULL_HANDLING = NullHandling.NATIVE;
private final Direction direction;
private final String property;
private final boolean ignoreCase;
private final NullHandling nullHandling;
/**
* Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
* {@link Sort#DEFAULT_DIRECTION}
*
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
*/
public Order(@Nullable Direction direction, String property) {
this(direction, property, DEFAULT_IGNORE_CASE, DEFAULT_NULL_HANDLING);
}
/**
* Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
* {@link Sort#DEFAULT_DIRECTION}
*
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
* @param nullHandling must not be {@literal null}.
*/
public Order(@Nullable Direction direction, String property, NullHandling nullHandlingHint) {
this(direction, property, DEFAULT_IGNORE_CASE, nullHandlingHint);
}
/**
* Creates a new {@link Order} instance. Takes a single property. Direction defaults to
* {@link Sort#DEFAULT_DIRECTION}.
*
* @param property must not be {@literal null} or empty.
* @deprecated since 2.0, use {@link Order#by(String)}.
*/
@Deprecated
public Order(String property) {
this(DEFAULT_DIRECTION, property);
}
/**
* Creates a new {@link Order} instance. Takes a single property. Direction defaults to
* {@link Sort#DEFAULT_DIRECTION}.
*
* @param property must not be {@literal null} or empty.
* @since 2.0
*/
public static Order by(String property) {
return new Order(DEFAULT_DIRECTION, property);
}
/**
* Creates a new {@link Order} instance. Takes a single property. Direction is {@link Direction#ASC} and
* NullHandling {@link NullHandling#NATIVE}.
*
* @param property must not be {@literal null} or empty.
* @since 2.0
*/
public static Order asc(String property) {
return new Order(Direction.ASC, property, DEFAULT_NULL_HANDLING);
}
/**
* Creates a new {@link Order} instance. Takes a single property. Direction is {@link Direction#ASC} and
* NullHandling {@link NullHandling#NATIVE}.
*
* @param property must not be {@literal null} or empty.
* @since 2.0
*/
public static Order desc(String property) {
return new Order(Direction.DESC, property, DEFAULT_NULL_HANDLING);
}
/**
* Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
* {@link Sort#DEFAULT_DIRECTION}
*
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
* @param ignoreCase true if sorting should be case insensitive. false if sorting should be case sensitive.
* @param nullHandling must not be {@literal null}.
* @since 1.7
*/
private Order(@Nullable Direction direction, String property, boolean ignoreCase, NullHandling nullHandling) {
if (!StringUtils.hasText(property)) {
throw new IllegalArgumentException("Property must not null or empty!");
}
this.direction = direction == null ? DEFAULT_DIRECTION : direction;
this.property = property;
this.ignoreCase = ignoreCase;
this.nullHandling = nullHandling;
}
/**
* Returns the order the property shall be sorted for.
*
* @return
*/
public Direction getDirection() {
return direction;
}
/**
* Returns the property to order for.
*
* @return
*/
public String getProperty() {
return property;
}
/**
* Returns whether sorting for this property shall be ascending.
*
* @return
*/
public boolean isAscending() {
return this.direction.isAscending();
}
/**
* Returns whether sorting for this property shall be descending.
*
* @return
* @since 1.13
*/
public boolean isDescending() {
return this.direction.isDescending();
}
/**
* Returns whether or not the sort will be case sensitive.
*
* @return
*/
public boolean isIgnoreCase() {
return ignoreCase;
}
/**
* Returns a new {@link Order} with the given {@link Direction}.
*
* @param direction
* @return
*/
public Order with(Direction direction) {
return new Order(direction, this.property, this.ignoreCase, this.nullHandling);
}
/**
* Returns a new {@link Order}
*
* @param property must not be {@literal null} or empty.
* @return
* @since 1.13
*/
public Order withProperty(String property) {
return new Order(this.direction, property, this.ignoreCase, this.nullHandling);
}
/**
* Returns a new {@link Sort} instance for the given properties.
*
* @param properties
* @return
*/
public Sort withProperties(String... properties) {
return Sort.by(this.direction, properties);
}
/**
* Returns a new {@link Order} with case insensitive sorting enabled.
*
* @return
*/
public Order ignoreCase() {
return new Order(direction, property, true, nullHandling);
}
/**
* Returns a {@link Order} with the given {@link NullHandling}.
*
* @param nullHandling can be {@literal null}.
* @return
* @since 1.8
*/
public Order with(NullHandling nullHandling) {
return new Order(direction, this.property, ignoreCase, nullHandling);
}
/**
* Returns a {@link Order} with {@link NullHandling#NULLS_FIRST} as null handling hint.
*
* @return
* @since 1.8
*/
public Order nullsFirst() {
return with(NullHandling.NULLS_FIRST);
}
/**
* Returns a {@link Order} with {@link NullHandling#NULLS_LAST} as null handling hint.
*
* @return
* @since 1.7
*/
public Order nullsLast() {
return with(NullHandling.NULLS_LAST);
}
/**
* Returns a {@link Order} with {@link NullHandling#NATIVE} as null handling hint.
*
* @return
* @since 1.7
*/
public Order nullsNative() {
return with(NullHandling.NATIVE);
}
/**
* Returns the used {@link NullHandling} hint, which can but may not be respected by the used datastore.
*
* @return
* @since 1.7
*/
public NullHandling getNullHandling() {
return nullHandling;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result = 31 * result + direction.hashCode();
result = 31 * result + property.hashCode();
result = 31 * result + (ignoreCase ? 1 : 0);
result = 31 * result + nullHandling.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Order)) {
return false;
}
Order that = (Order) obj;
return this.direction.equals(that.direction) && this.property.equals(that.property)
&& this.ignoreCase == that.ignoreCase && this.nullHandling.equals(that.nullHandling);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
String result = String.format("%s: %s", property, direction);
if (!NullHandling.NATIVE.equals(nullHandling)) {
result += ", " + nullHandling;
}
if (ignoreCase) {
result += ", ignoring case";
}
return result;
}
}
一个排序字段对应一个方向。
public Order(@Nullable Direction direction, String property) {
this(direction, property, DEFAULT_IGNORE_CASE, DEFAULT_NULL_HANDLING);
}
即可以是实现多个排序字段不同方法的功能
修改测试方法:
@Test
public void findAllTest() {
Order idOrder = new Order(Direction.DESC, "id");
Order nameOrder = new Order(Direction.ASC, "name");
PageRequest pageRequest = PageRequest.of(1, 1, Sort.by(idOrder, nameOrder));
SysUser sysUser = new SysUser();
sysUser.setName("m");
Page<SysUser> page = userRepository.findAll(Example.of(sysUser,ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)), pageRequest);
List<SysUser> content = page.getContent();
log.info("content:{}", content);
}
Hibernate: select sysuser0_.id as id1_3_, sysuser0_.create_date as create_d2_3_, sysuser0_.modify_date as modify_d3_3_, sysuser0_.name as name4_3_, sysuser0_.password as password5_3_ from sys_user sysuser0_ where sysuser0_.name like ? order by sysuser0_.id desc, sysuser0_.name asc limit ?, ?
Hibernate: select roles0_.user_id as user_id5_4_0_, roles0_.role_id as role_id4_4_0_, sysrole1_.id as id1_2_1_, sysrole1_.create_date as create_d2_2_1_, sysrole1_.modify_date as modify_d3_2_1_, sysrole1_.name as name4_2_1_, sysrole1_.parent_id as parent_i5_2_1_ from sys_user_role roles0_ inner join sys_role sysrole1_ on roles0_.role_id=sysrole1_.id where roles0_.user_id=?
Hibernate: select menus0_.role_id as role_id5_1_0_, menus0_.menu_id as menu_id4_1_0_, sysmenu1_.id as id1_0_1_, sysmenu1_.create_date as create_d2_0_1_, sysmenu1_.modify_date as modify_d3_0_1_, sysmenu1_.icon as icon4_0_1_, sysmenu1_.name as name5_0_1_, sysmenu1_.order_num as order_nu6_0_1_, sysmenu1_.parent_id as parent_i7_0_1_, sysmenu1_.permission as permissi8_0_1_, sysmenu1_.type as type9_0_1_, sysmenu1_.url as url10_0_1_ from sys_menu_role menus0_ inner join sys_menu sysmenu1_ on menus0_.menu_id=sysmenu1_.id where menus0_.role_id=?
Hibernate: select menus0_.role_id as role_id5_1_0_, menus0_.menu_id as menu_id4_1_0_, sysmenu1_.id as id1_0_1_, sysmenu1_.create_date as create_d2_0_1_, sysmenu1_.modify_date as modify_d3_0_1_, sysmenu1_.icon as icon4_0_1_, sysmenu1_.name as name5_0_1_, sysmenu1_.order_num as order_nu6_0_1_, sysmenu1_.parent_id as parent_i7_0_1_, sysmenu1_.permission as permissi8_0_1_, sysmenu1_.type as type9_0_1_, sysmenu1_.url as url10_0_1_ from sys_menu_role menus0_ inner join sys_menu sysmenu1_ on menus0_.menu_id=sysmenu1_.id where menus0_.role_id=?
Hibernate: select count(sysuser0_.id) as col_0_0_ from sys_user sysuser0_ where sysuser0_.name like ?
第一行:
where sysuser0_.name like ? order by sysuser0_.id desc, sysuser0_.name asc limit ?, ?
源码分页方法:
/**
* Returns a {@link Page} of entities matching the given {@link Example}. In case no match could be found, an empty
* {@link Page} is returned.
*
* @param example must not be {@literal null}.
* @param pageable can be {@literal null}.
* @return a {@link Page} of entities matching the given {@link Example}.
*/
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
第二个参数分页是知道了 ,第一个参数Example条件参数呢?
Example接口源码
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import org.springframework.data.util.ProxyUtils;
/**
* Support for query by example (QBE). An {@link Example} takes a {@code probe} to define the example. Matching options
* and type safety can be tuned using {@link ExampleMatcher}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Oliver Gierke
* @param <T> the type of the probe.
* @since 1.12
*/
public interface Example<T> {
/**
* Create a new {@link Example} including all non-null properties by default.
*
* @param probe must not be {@literal null}.
* @return
*/
static <T> Example<T> of(T probe) {
return new TypedExample<>(probe, ExampleMatcher.matching());
}
/**
* Create a new {@link Example} using the given {@link ExampleMatcher}.
*
* @param probe must not be {@literal null}.
* @param matcher must not be {@literal null}.
* @return
*/
static <T> Example<T> of(T probe, ExampleMatcher matcher) {
return new TypedExample<>(probe, matcher);
}
/**
* Get the example used.
*
* @return never {@literal null}.
*/
T getProbe();
/**
* Get the {@link ExampleMatcher} used.
*
* @return never {@literal null}.
*/
ExampleMatcher getMatcher();
/**
* Get the actual type for the probe used. This is usually the given class, but the original class in case of a
* CGLIB-generated subclass.
*
* @return
* @see ProxyUtils#getUserClass(Class)
*/
@SuppressWarnings("unchecked")
default Class<T> getProbeType() {
return (Class<T>) ProxyUtils.getUserClass(getProbe().getClass());
}
}
泛型接口 T其实指的就是要查询的实体,使用静态方法new TypedExample<>(probe, ExampleMatcher.matching());创建实现类,除了实体外还有一个匹配机制参数。有默认机制。
匹配机制源码
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Specification for property path matching to use in query by example (QBE). An {@link ExampleMatcher} can be created
* for a {@link Class object type}. Instances of {@link ExampleMatcher} can be either {@link #matchingAll()} or
* {@link #matchingAny()} and settings can be tuned {@code with...} methods in a fluent style. {@code with...} methods
* return a copy of the {@link ExampleMatcher} instance with the specified setting. Null-handling defaults to
* {@link NullHandler#IGNORE} and case-sensitive {@link StringMatcher#DEFAULT} string matching.
* <p>
* This class is immutable.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Oliver Gierke
* @author Jens Schauder
* @since 1.12
*/
public interface ExampleMatcher {
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default matching <strong>all</strong>
* predicates derived from the example.
*
* @return new instance of {@link ExampleMatcher}.
* @see #matchingAll()
*/
static ExampleMatcher matching() {
return matchingAll();
}
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default matching <strong>any</strong>
* predicate derived from the example.
*
* @return new instance of {@link ExampleMatcher}.
*/
static ExampleMatcher matchingAny() {
return new TypedExampleMatcher().withMode(MatchMode.ANY);
}
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default matching <strong>all</strong>
* predicates derived from the example.
*
* @return new instance of {@link ExampleMatcher}.
*/
static ExampleMatcher matchingAll() {
return new TypedExampleMatcher().withMode(MatchMode.ALL);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code propertyPaths}. This instance is immutable
* and unaffected by this method call.
*
* @param ignoredPaths must not be {@literal null} and not empty.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withIgnorePaths(String... ignoredPaths);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified string matching of {@code defaultStringMatcher}.
* This instance is immutable and unaffected by this method call.
*
* @param defaultStringMatcher must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
/**
* Returns a copy of this {@link ExampleMatcher} with ignoring case sensitivity by default. This instance is immutable
* and unaffected by this method call.
*
* @return new instance of {@link ExampleMatcher}.
*/
default ExampleMatcher withIgnoreCase() {
return withIgnoreCase(true);
}
/**
* Returns a copy of this {@link ExampleMatcher} with {@code defaultIgnoreCase}. This instance is immutable and
* unaffected by this method call.
*
* @param defaultIgnoreCase
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
*
* @param propertyPath must not be {@literal null}.
* @param matcherConfigurer callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
default ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(matcherConfigurer, "MatcherConfigurer must not be empty!");
GenericPropertyMatcher genericPropertyMatcher = new GenericPropertyMatcher();
matcherConfigurer.configureMatcher(genericPropertyMatcher);
return withMatcher(propertyPath, genericPropertyMatcher);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
*
* @param propertyPath must not be {@literal null}.
* @param genericPropertyMatcher callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
* {@code propertyPath}.
*
* @param propertyPath must not be {@literal null}.
* @param propertyValueTransformer must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
/**
* Returns a copy of this {@link ExampleMatcher} with ignore case sensitivity for the {@code propertyPaths}. This
* instance is immutable and unaffected by this method call.
*
* @param propertyPaths must not be {@literal null} and not empty.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withIgnoreCase(String... propertyPaths);
/**
* Returns a copy of this {@link ExampleMatcher} with treatment for {@literal null} values of
* {@link NullHandler#INCLUDE} . This instance is immutable and unaffected by this method call.
*
* @return new instance of {@link ExampleMatcher}.
*/
default ExampleMatcher withIncludeNullValues() {
return withNullHandler(NullHandler.INCLUDE);
}
/**
* Returns a copy of this {@link ExampleMatcher} with treatment for {@literal null} values of
* {@link NullHandler#IGNORE}. This instance is immutable and unaffected by this method call.
*
* @return new instance of {@link ExampleMatcher}.
*/
default ExampleMatcher withIgnoreNullValues() {
return withNullHandler(NullHandler.IGNORE);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code nullHandler}. This instance is immutable
* and unaffected by this method call.
*
* @param nullHandler must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withNullHandler(NullHandler nullHandler);
/**
* Get defined null handling.
*
* @return never {@literal null}
*/
NullHandler getNullHandler();
/**
* Get defined {@link ExampleMatcher.StringMatcher}.
*
* @return never {@literal null}.
*/
StringMatcher getDefaultStringMatcher();
/**
* @return {@literal true} if {@link String} should be matched with ignore case option.
*/
boolean isIgnoreCaseEnabled();
/**
* @param path must not be {@literal null}.
* @return return {@literal true} if path was set to be ignored.
*/
default boolean isIgnoredPath(String path) {
return getIgnoredPaths().contains(path);
}
/**
* @return unmodifiable {@link Set} of ignored paths.
*/
Set<String> getIgnoredPaths();
/**
* @return the {@link PropertySpecifiers} within the {@link ExampleMatcher}.
*/
PropertySpecifiers getPropertySpecifiers();
/**
* Returns whether all of the predicates of the {@link Example} are supposed to match. If {@literal false} is
* returned, it's sufficient if any of the predicates derived from the {@link Example} match.
*
* @return whether all of the predicates of the {@link Example} are supposed to match or any of them is sufficient.
*/
default boolean isAllMatching() {
return getMatchMode().equals(MatchMode.ALL);
}
/**
* Returns whether it's sufficient that any of the predicates of the {@link Example} match. If {@literal false} is
* returned, all predicates derived from the example need to match to produce results.
*
* @return whether it's sufficient that any of the predicates of the {@link Example} match or all need to match.
*/
default boolean isAnyMatching() {
return getMatchMode().equals(MatchMode.ANY);
}
/**
* Get the match mode of the {@link ExampleMatcher}.
*
* @return never {@literal null}.
* @since 2.0
*/
MatchMode getMatchMode();
/**
* Null handling for creating criterion out of an {@link Example}.
*
* @author Christoph Strobl
*/
enum NullHandler {
INCLUDE, IGNORE
}
/**
* Callback to configure a matcher.
*
* @author Mark Paluch
* @param <T>
*/
interface MatcherConfigurer<T> {
void configureMatcher(T matcher);
}
/**
* A generic property matcher that specifies {@link StringMatcher string matching} and case sensitivity.
*
* @author Mark Paluch
*/
@EqualsAndHashCode
class GenericPropertyMatcher {
@Nullable StringMatcher stringMatcher = null;
@Nullable Boolean ignoreCase = null;
PropertyValueTransformer valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
/**
* Creates an unconfigured {@link GenericPropertyMatcher}.
*/
public GenericPropertyMatcher() {}
/**
* Creates a new {@link GenericPropertyMatcher} with a {@link StringMatcher} and {@code ignoreCase}.
*
* @param stringMatcher must not be {@literal null}.
* @param ignoreCase
* @return
*/
public static GenericPropertyMatcher of(StringMatcher stringMatcher, boolean ignoreCase) {
return new GenericPropertyMatcher().stringMatcher(stringMatcher).ignoreCase(ignoreCase);
}
/**
* Creates a new {@link GenericPropertyMatcher} with a {@link StringMatcher} and {@code ignoreCase}.
*
* @param stringMatcher must not be {@literal null}.
* @return
*/
public static GenericPropertyMatcher of(StringMatcher stringMatcher) {
return new GenericPropertyMatcher().stringMatcher(stringMatcher);
}
/**
* Sets ignores case to {@literal true}.
*
* @return
*/
public GenericPropertyMatcher ignoreCase() {
this.ignoreCase = true;
return this;
}
/**
* Sets ignores case to {@code ignoreCase}.
*
* @param ignoreCase
* @return
*/
public GenericPropertyMatcher ignoreCase(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
return this;
}
/**
* Sets ignores case to {@literal false}.
*
* @return
*/
public GenericPropertyMatcher caseSensitive() {
this.ignoreCase = false;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#CONTAINING}.
*
* @return
*/
public GenericPropertyMatcher contains() {
this.stringMatcher = StringMatcher.CONTAINING;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#ENDING}.
*
* @return
*/
public GenericPropertyMatcher endsWith() {
this.stringMatcher = StringMatcher.ENDING;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#STARTING}.
*
* @return
*/
public GenericPropertyMatcher startsWith() {
this.stringMatcher = StringMatcher.STARTING;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#EXACT}.
*
* @return
*/
public GenericPropertyMatcher exact() {
this.stringMatcher = StringMatcher.EXACT;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#DEFAULT}.
*
* @return
*/
public GenericPropertyMatcher storeDefaultMatching() {
this.stringMatcher = StringMatcher.DEFAULT;
return this;
}
/**
* Sets string matcher to {@link StringMatcher#REGEX}.
*
* @return
*/
public GenericPropertyMatcher regex() {
this.stringMatcher = StringMatcher.REGEX;
return this;
}
/**
* Sets string matcher to {@code stringMatcher}.
*
* @param stringMatcher must not be {@literal null}.
* @return
*/
public GenericPropertyMatcher stringMatcher(StringMatcher stringMatcher) {
Assert.notNull(stringMatcher, "StringMatcher must not be null!");
this.stringMatcher = stringMatcher;
return this;
}
/**
* Sets the {@link PropertyValueTransformer} to {@code propertyValueTransformer}.
*
* @param propertyValueTransformer must not be {@literal null}.
* @return
*/
public GenericPropertyMatcher transform(PropertyValueTransformer propertyValueTransformer) {
Assert.notNull(propertyValueTransformer, "PropertyValueTransformer must not be null!");
this.valueTransformer = propertyValueTransformer;
return this;
}
}
/**
* Predefined property matchers to create a {@link GenericPropertyMatcher}.
*
* @author Mark Paluch
*/
class GenericPropertyMatchers {
/**
* Creates a {@link GenericPropertyMatcher} that matches string case insensitive.
*
* @return
*/
public static GenericPropertyMatcher ignoreCase() {
return new GenericPropertyMatcher().ignoreCase();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string case sensitive.
*
* @return
*/
public static GenericPropertyMatcher caseSensitive() {
return new GenericPropertyMatcher().caseSensitive();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#CONTAINING}.
*
* @return
*/
public static GenericPropertyMatcher contains() {
return new GenericPropertyMatcher().contains();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#ENDING}.
*
* @return
*/
public static GenericPropertyMatcher endsWith() {
return new GenericPropertyMatcher().endsWith();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#STARTING}.
*
* @return
*/
public static GenericPropertyMatcher startsWith() {
return new GenericPropertyMatcher().startsWith();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#EXACT}.
*
* @return
*/
public static GenericPropertyMatcher exact() {
return new GenericPropertyMatcher().exact();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#DEFAULT}.
*
* @return
*/
public static GenericPropertyMatcher storeDefaultMatching() {
return new GenericPropertyMatcher().storeDefaultMatching();
}
/**
* Creates a {@link GenericPropertyMatcher} that matches string using {@link StringMatcher#REGEX}.
*
* @return
*/
public static GenericPropertyMatcher regex() {
return new GenericPropertyMatcher().regex();
}
}
/**
* Match modes for treatment of {@link String} values.
*
* @author Christoph Strobl
* @author Jens Schauder
*/
enum StringMatcher {
/**
* Store specific default.
*/
DEFAULT,
/**
* Matches the exact string
*/
EXACT,
/**
* Matches string starting with pattern
*/
STARTING,
/**
* Matches string ending with pattern
*/
ENDING,
/**
* Matches string containing pattern
*/
CONTAINING,
/**
* Treats strings as regular expression patterns
*/
REGEX;
}
/**
* Allows to transform the property value before it is used in the query.
*/
interface PropertyValueTransformer extends Function<Optional<Object>, Optional<Object>> {
/**
* For backwards compatibility of clients used to invoke Spring's Converter interface.
*
* @param source
* @return
*/
@Deprecated
default Object convert(Object source) {
return apply(Optional.ofNullable(source)).orElse(null);
}
}
/**
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.12
*/
enum NoOpPropertyValueTransformer implements ExampleMatcher.PropertyValueTransformer {
INSTANCE;
/*
* (non-Javadoc)
* @see java.util.function.Function#apply(java.lang.Object)
*/
@Override
@SuppressWarnings("null")
public Optional<Object> apply(Optional<Object> source) {
return source;
}
}
/**
* Define specific property handling for a Dot-Path.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.12
*/
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
class PropertySpecifier {
String path;
@Nullable StringMatcher stringMatcher;
@Nullable Boolean ignoreCase;
PropertyValueTransformer valueTransformer;
/**
* Creates new {@link PropertySpecifier} for given path.
*
* @param path Dot-Path to the property. Must not be {@literal null}.
*/
PropertySpecifier(String path) {
Assert.hasText(path, "Path must not be null/empty!");
this.path = path;
this.stringMatcher = null;
this.ignoreCase = null;
this.valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
}
/**
* Creates a new {@link PropertySpecifier} containing all values from the current instance and sets
* {@link StringMatcher} in the returned instance.
*
* @param stringMatcher must not be {@literal null}.
* @return
*/
public PropertySpecifier withStringMatcher(StringMatcher stringMatcher) {
Assert.notNull(stringMatcher, "StringMatcher must not be null!");
return new PropertySpecifier(this.path, stringMatcher, this.ignoreCase, this.valueTransformer);
}
/**
* Creates a new {@link PropertySpecifier} containing all values from the current instance and sets
* {@code ignoreCase}.
*
* @param ignoreCase must not be {@literal null}.
* @return
*/
public PropertySpecifier withIgnoreCase(boolean ignoreCase) {
return new PropertySpecifier(this.path, this.stringMatcher, ignoreCase, this.valueTransformer);
}
/**
* Creates a new {@link PropertySpecifier} containing all values from the current instance and sets
* {@link PropertyValueTransformer} in the returned instance.
*
* @param valueTransformer must not be {@literal null}.
* @return
*/
public PropertySpecifier withValueTransformer(PropertyValueTransformer valueTransformer) {
Assert.notNull(valueTransformer, "PropertyValueTransformer must not be null!");
return new PropertySpecifier(this.path, this.stringMatcher, this.ignoreCase, valueTransformer);
}
/**
* Get the properties Dot-Path.
*
* @return never {@literal null}.
*/
public String getPath() {
return path;
}
/**
* Get the {@link StringMatcher}.
*
* @return can be {@literal null}.
*/
@Nullable
public StringMatcher getStringMatcher() {
return stringMatcher;
}
/**
* @return {@literal null} if not set.
*/
@Nullable
public Boolean getIgnoreCase() {
return ignoreCase;
}
/**
* Get the property transformer to be applied.
*
* @return never {@literal null}.
*/
public PropertyValueTransformer getPropertyValueTransformer() {
return valueTransformer == null ? NoOpPropertyValueTransformer.INSTANCE : valueTransformer;
}
/**
* Transforms a given source using the {@link PropertyValueTransformer}.
*
* @param source
* @return
* @deprecated since 2.0, use {@link #transformValue(Optional)} instead.
*/
@Deprecated
public Object transformValue(Object source) {
return transformValue(Optional.ofNullable(source)).orElse(null);
}
/**
* Transforms a given source using the {@link PropertyValueTransformer}.
*
* @param source
* @return
*/
public Optional<Object> transformValue(Optional<Object> source) {
return getPropertyValueTransformer().apply(source);
}
}
/**
* Define specific property handling for Dot-Paths.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.12
*/
@EqualsAndHashCode
class PropertySpecifiers {
private final Map<String, PropertySpecifier> propertySpecifiers = new LinkedHashMap<>();
PropertySpecifiers() {}
PropertySpecifiers(PropertySpecifiers propertySpecifiers) {
this.propertySpecifiers.putAll(propertySpecifiers.propertySpecifiers);
}
public void add(PropertySpecifier specifier) {
Assert.notNull(specifier, "PropertySpecifier must not be null!");
propertySpecifiers.put(specifier.getPath(), specifier);
}
public boolean hasSpecifierForPath(String path) {
return propertySpecifiers.containsKey(path);
}
public PropertySpecifier getForPath(String path) {
return propertySpecifiers.get(path);
}
public boolean hasValues() {
return !propertySpecifiers.isEmpty();
}
public Collection<PropertySpecifier> getSpecifiers() {
return propertySpecifiers.values();
}
}
/**
* The match modes to expose so that clients can find about how to concatenate the predicates.
*
* @author Oliver Gierke
* @since 1.13
* @see ExampleMatcher#isAllMatching()
*/
enum MatchMode {
ALL, ANY;
}
}
ExampleMatcher.matching()的配置机制是:new TypedExampleMatcher().withMode(MatchMode.ALL);(全部匹配)
修改测试方法:
Page<SysUser> page = userRepository.findAll(Example.of(sysUser), pageRequest);
执行的SQL将不再是like ;
where sysuser0_.name=? order by sysuser0_.id desc, sysuser0_.name asc limit ?, ?
实际开发需求更多的使用了模糊查询
/**
* Returns a copy of this {@link ExampleMatcher} with the specified string matching of {@code defaultStringMatcher}.
* This instance is immutable and unaffected by this method call.
*
* @param defaultStringMatcher must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
找到这个方法设置字符串的匹配规则; 有如下规则
enum StringMatcher {
/**
* Store specific default.
*/
DEFAULT,
/**
* Matches the exact string
*/
EXACT,
/**
* Matches string starting with pattern
*/
STARTING,
/**
* Matches string ending with pattern
*/
ENDING,
/**
* Matches string containing pattern
*/
CONTAINING,
/**
* Treats strings as regular expression patterns
*/
REGEX;
}
选择匹配方法 :CONTAINING --包含字符串即可
测试方法:
Page<SysUser> page = userRepository.findAll(Example.of(sysUser, ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)), pageRequest);
优化一下:
自定义枚举
package study.springboot.domain.enums.jpa;
import org.springframework.data.domain.ExampleMatcher;
/**
* @Date 2019年4月11日
* @Sgin SelectFlagEnum
* @Author Bertram.Wang
*/
public enum SelectFlagEnum {
LIKE(ExampleMatcher.matching().withStringMatcher( ExampleMatcher.StringMatcher.CONTAINING));
public ExampleMatcher exampleMatcher;
private SelectFlagEnum(ExampleMatcher exampleMatcher) {
this.exampleMatcher = exampleMatcher;
}
}
测试方法修改为:
@Test
public void findAllTest() {
Order idOrder = new Order(Direction.DESC, "id");
Order nameOrder = new Order(Direction.ASC, "name");
PageRequest pageRequest = PageRequest.of(1, 1, Sort.by(idOrder, nameOrder));
SysUser sysUser = new SysUser();
sysUser.setName("m");
Page<SysUser> page = userRepository.findAll(Example.of(sysUser, SelectFlagEnum.LIKE.exampleMatcher), pageRequest);
List<SysUser> content = page.getContent();
log.info("content:{}", content);
}
公用字段自动填充的问题:
/**
* @Date 2019年4月11日
* @Sgin BaseEntity
* @Author Bertram.Wang
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="create_date")
@CreatedDate
private Date createDate;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="modify_date")
@LastModifiedDate
private Date modifyDate;
}
创建时间和修改时间自动填充的问题?
加上注解: @LastModifiedDate , @CreatedDate
实体类加上监听 : @EntityListeners(AuditingEntityListener.class)
启动类上: @EnableJpaAuditing
加上这些后新增方法确实能自动填充创建时间和修改时间。
Hibernate: insert into sys_user (create_date, modify_date, name, password) values (?, ?, ?, ?)
Hibernate: insert into sys_user_role (user_id, role_id) values (?, ?)
第一行自动填充了,但是级联插入的关系表却没有填充创建时间和修改时间,导致抛出错误o.h.engine.jdbc.spi.SqlExceptionHelper : Field 'create_date' doesn't have a default value
没有找到代码解决方法。
最后处理方式是:数据库设置默认值。