美文网首页
SpringDataJpa增删改查

SpringDataJpa增删改查

作者: Bertram_Wang | 来源:发表于2019-04-14 16:47 被阅读0次

    技术背景:
            开发工具: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

    没有找到代码解决方法。
    最后处理方式是:数据库设置默认值。

    相关文章

      网友评论

          本文标题:SpringDataJpa增删改查

          本文链接:https://www.haomeiwen.com/subject/fiplwqtx.html