SpringData JPA

3 篇文章 0 订阅

1、SpringDataJPA

JPA是Java EE 5规范中提出的Java持久化接口,Hibernate是一个ORM框架,Spring Data JPA是基于JPA规范的,而Hibernate是一个ORM框架,它实现了JPA规范。因此,Spring Data JPA是基于Hibernate的,但是它提供了更多的功能和更好的性能 。
在这里插入图片描述

2、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!--spring 和 spring data jpa的配置-->

    <!-- 1.创建entityManagerFactory对象交给spring容器管理-->
    <bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--配置的扫描的包(实体类所在的包) -->
        <property name="packagesToScan" value="cn.itcast.domain" />
        <!-- jpa的实现厂家 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <!--jpa的供应商适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--配置是否自动创建数据库表 -->
                <property name="generateDdl" value="false" />
                <!--指定数据库类型 -->
                <property name="database" value="MYSQL" />
                <!--数据库方言:支持的特有语法 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                <!--是否显示sql -->
                <property name="showSql" value="true" />
            </bean>
        </property>

        <!--jpa的方言 :高级的特性 -->
        <property name="jpaDialect" >
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>

    </bean>

    <!--2.创建数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="sys as sysdba"></property>
        <property name="password" value="root"></property>
        <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521/orcl" ></property>
        <property name="driverClass" value="oracle.jdbc.OracleDriver"></property>
    </bean>

    <!--3.整合spring dataJpa-->
    <jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager"
                   entity-manager-factory-ref="entityManagerFactoty" ></jpa:repositories>

    <!--4.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoty"></property>
    </bean>

    <!-- 4.txAdvice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 5.aop-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
    </aop:config>


    <!--5.声明式事务 -->

    <!-- 6. 配置包扫描,扫描包下的所有注解-->
    <context:component-scan base-package="cn.itcast" ></context:component-scan>
</beans>

3、实体类

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *		 	1.实体类和表的映射关系
 *      		@Eitity		声明实体类
 *      		@Table		指定数据库表
 * 			2.类中属性和表中字段的映射关系
 *    		 	@Id			对应的数据库表的主键
 *    	 	 	@GeneratedValue		对应的数据库的主键生成策略,不一样的数据库,生成策略不一样
 *     		 	@Column 	对应的数据库表字段
 * 
 *      * 所有的注解都是使用JPA的规范提供的注解,
 *      * 所以在导入注解包的时候,一定要导入javax.persistence下的
 */
@Entity //声明实体类
@Table(name="cst_customer") //建立实体类和表的映射关系
public class Customer {
    
    @Id//声明当前私有属性为主键
    @GeneratedValue(strategy=GenerationType.SEQUENCE) //配置主键的生成策略
    @Column(name="cust_id") //指定和表中cust_id字段的映射关系
    private Long custId;
    
	//省略get\set
}


4、Dao

4.1 jpql和sql查询

  dao继承JpaRepositoryJpaSpecificationExecutor
继承了JpaRepository,就可以实现基本的操作,
继承了JpaSpecificationExecutor,就可以实现复杂查询(分页),
继承了这两个接口不满足需求的话,可以自己写方法,通过@Query来写数据库操作的语句
@Query查询语句分为jpql语句和sql语句,使用sql要在@Query中使用nativeQuery = true
jpql:语句中的对象和属性都是实体类中类名和属性名
sql:语句中的对象和属性都是数据库表中表名和字段名

import cn.itcast.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
 * 符合SpringDataJpa的dao层接口规范
 *      JpaRepository<操作的实体类类型,实体类中主键属性的类型>
 *          * 封装了基本CRUD操作
 *      JpaSpecificationExecutor<操作的实体类类型>
 *          * 封装了复杂查询(分页)
 */
public interface CustomerDao extends JpaRepository<Customer,Long> ,JpaSpecificationExecutor<Customer> {

    /**
     * 案例:根据客户名称查询客户
     *      使用jpql的形式查询
     *  jpql:from Customer where custName = ?
     *
     *  配置jpql语句,使用的@Query注解
     */
    @Query(value="from Customer where custName = ?1")
    public Customer findJpql(String custName);


    /**
     * 案例:根据客户名称和客户id查询客户
     *      jpql: from Customer where custName = ? and custId = ?
     *
     *  对于多个占位符参数
     *      赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
     *
     *  可以指定占位符参数的位置
     *      ? 索引的方式,指定此占位的取值来源
     */
    @Query(value = "from Customer where custName = ?2 and custId = ?1")
    public Customer findCustNameAndId(Long id,String name);

    /**
     * 使用jpql完成更新操作
     *      案例 : 根据id更新,客户的名称
     *          更新4号客户的名称,将名称改为“程序员”
     *
     *  sql  :update cst_customer set cust_name = ? where cust_id = ?
     *  jpql : update Customer set custName = ? where custId = ?
     *
     *  @Query : 代表的是进行查询
     *      * 声明此方法是用来进行更新操作
     *  @Modifying
     *      * 当前执行的是一个更新操作
     *
     */
    @Query(value = " update Customer set custName = ?2 where custId = ?1 ")
    @Modifying
    public void updateCustomer(long custId,String custName);


    /**
     * 使用sql的形式查询:
     *     查询全部的客户
     *  sql : select * from cst_customer;
     *  Query : 配置sql查询
     *      value : sql语句
     *      nativeQuery : 查询方式
     *          true : sql查询
     *          false:jpql查询
     *
     */    /**
     * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
     * Returns root of the resulting tree (where root can consist of just a single node if the current
     * event is a value event, not container).
     *
     * @param file File of which contents to parse as JSON for building a tree instance
     *
     * @since 1.9
     */
    //@Query(value = " select * from cst_customer" ,nativeQuery = true)
    @Query(value="select * from cst_customer where cust_name like ?1",nativeQuery = true)
    public List<Object [] > findSql(String name);


    /**
     * 方法名的约定:
     *      findBy : 查询
     *            对象中的属性名(首字母大写) : 查询的条件
     *            CustName
     *            * 默认情况 : 使用 等于的方式查询
     *                  特殊的查询方式
     *
     *  findByCustName   --   根据客户名称查询
     *
     *  再springdataJpa的运行阶段
     *          会根据方法名称进行解析  findBy    from  xxx(实体类)
     *                                      属性名称      where  custName =
     *
     *      1.findBy  + 属性名称 (根据属性名称进行完成匹配的查询=)
     *      2.findBy  + 属性名称 + “查询方式(Like | isnull)”
     *          findByCustNameLike
     *      3.多条件查询
     *          findBy + 属性名 + “查询方式”   + “多条件的连接符(and|or)”  + 属性名 + “查询方式”
     */
    public Customer findByCustName(String custName);

	//方法命名方式查询
    public List<Customer> findByCustNameLike(String custName);

    方法命名方式查询,使用客户名称模糊匹配和客户所属行业精准匹配的查询
    public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
}

4.2 方法命名方式查询

KeywordSampleJPQL
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age ⇐ ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNullfindByAgeIsNull… where x.age is null
IsNotNull,NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection age)… where x.age not in ?1
TRUEfindByActiveTrue()… where x.active = true
FALSEfindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstame) = UPPER(?1)

5、测试

5.1 测试自己写的jpql语句

import cn.itcast.dao.CustomerDao;
import cn.itcast.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class JpqlTest {
    @Autowired
    private CustomerDao customerDao;

    @Test
    public void  testFindJPQL() {
        Customer customer = customerDao.findJpql("张三");
        System.out.println(customer);
    }


    @Test
    public void testFindCustNameAndId() {
       // Customer customer =  customerDao.findCustNameAndId("张三",1l);
        Customer customer =  customerDao.findCustNameAndId(1l,"张三");
        System.out.println(customer);
    }

    /**
     * 测试jpql的更新操作
     *  * springDataJpa中使用jpql完成 更新/删除操作
     *         * 需要手动添加事务的支持
     *         * 默认会执行结束之后,回滚事务
     *   @Rollback : 设置是否自动回滚
     *          false | true
     */
    @Test
    @Transactional //添加事务的支持
    @Rollback(value = false)
    public void testUpdateCustomer() {
        customerDao.updateCustomer(4l,"程序员");
    }

    //测试sql查询
    @Test
    public void testFindSql() {
        List<Object[]> list = customerDao.findSql("张三%");
        for(Object [] obj : list) {
            System.out.println(Arrays.toString(obj));
        }
    }


    //测试方法命名规则的查询
    @Test
    public void testNaming() {
        Customer customer = customerDao.findByCustName("张三");
        System.out.println(customer);
    }


    //测试方法命名规则的查询
    @Test
    public void testFindByCustNameLike() {
        List<Customer> list = customerDao.findByCustNameLike("张三%");
        for (Customer customer : list) {
            System.out.println(customer);
        }
    }


    //测试方法命名规则的查询
    @Test
    public void testFindByCustNameLikeAndCustIndustry() {
        Customer customer = customerDao.findByCustNameLikeAndCustIndustry("张三%", "it");
        System.out.println(customer);
    }
}

5.2 测试继承的类中的方法

import cn.itcast.dao.CustomerDao;
import cn.itcast.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class CustomerDaoTest {
    @Autowired
    private CustomerDao customerDao;

    /**
     * 根据id查询
     */
    @Test
    public void testFindOne() {
        Customer customer = customerDao.findOne(4l);
        System.out.println(customer);
    }

    /**
     * save : 保存或者更新
     *      根据传递的对象是否存在主键id,
     *      如果没有id主键属性:保存
     *      存在id主键属性,根据id查询数据,更新数据
     */
    @Test
    public void testSave() {
        Customer customer  = new Customer();
        customer.setCustName("程序员");
        customer.setCustLevel("vip");
        customer.setCustIndustry("it");
        customerDao.save(customer);
    }

    @Test
    public void testUpdate() {
        Customer customer  = new Customer();
        customer.setCustId(4l);
        customer.setCustName("程序员很厉害");
        customerDao.save(customer);
    }

    @Test
    public void testDelete () {
        customerDao.delete(3l);
    }


    /**
     * 查询所有
     */
    @Test
    public void testFindAll() {
        List<Customer> list = customerDao.findAll();
        for(Customer customer : list) {
            System.out.println(customer);
        }
    }

    /**
     * 测试统计查询:查询客户的总数量
     *      count:统计总条数
     */
    @Test
    public void testCount() {
        long count = customerDao.count();//查询全部的客户数量
        System.out.println(count);
    }

    /**
     * 测试:判断id为4的客户是否存在
     *      1. 可以查询以下id为4的客户
     *          如果值为空,代表不存在,如果不为空,代表存在
     *      2. 判断数据库中id为4的客户的数量
     *          如果数量为0,代表不存在,如果大于0,代表存在
     */
    @Test
    public void  testExists() {
        boolean exists = customerDao.exists(4l);
        System.out.println("id为4的客户 是否存在:"+exists);
    }


    /**
     * 根据id从数据库查询
     *      @Transactional : 保证getOne正常运行
     *
     *  findOne:
     *      em.find()           :立即加载
     *  getOne:
     *      em.getReference     :延迟加载
     *      * 返回的是一个客户的动态代理对象
     *      * 什么时候用,什么时候查询
     */
    @Test
    @Transactional
    public void  testGetOne() {
        Customer customer = customerDao.getOne(4l);
        System.out.println(customer);
    }

}

6、Specifications动态查询

  之前dao实现了两个接口:JpaRepositoryJpaSpecificationExecutor,以上的示例只是展示了JpaRepository,接下来就是JpaSpecificationExecutor
  有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

6.1 JpaSpecificationExecutor中的方法

/*
 * Copyright 2008-2011 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.jpa.repository;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

/**
 * Interface to allow execution of {@link Specification}s based on the JPA criteria API.
 * 
 * @author Oliver Gierke
 */
public interface JpaSpecificationExecutor<T> {

	/**
	 * Returns a single entity matching the given {@link Specification}.
	 * 根据条件查询一个对象
	 * @param spec
	 * @return
	 */
	T findOne(Specification<T> spec);

	/**
	 * Returns all entities matching the given {@link Specification}.
	 * 根据条件查询集合
	 * @param spec
	 * @return
	 */
	List<T> findAll(Specification<T> spec);

	/**
	 * Returns a {@link Page} of entities matching the given {@link Specification}.
	 * 根据条件分页查询
	 * @param spec
	 * @param pageable
	 * @return
	 */
	Page<T> findAll(Specification<T> spec, Pageable pageable);

	/**
	 * Returns all entities matching the given {@link Specification} and {@link Sort}.
	 * 排序查询查询	
	 * @param spec
	 * @param sort
	 * @return
	 */
	List<T> findAll(Specification<T> spec, Sort sort);

	/**
	 * Returns the number of instances that the given {@link Specification} will return.
	 * 统计查询
	 * @param spec the {@link Specification} to count instances for
	 * @return the number of instances
	 */
	long count(Specification<T> spec);
}

6.2 Specification

对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件
Specification接口中只定义了如下一个方法:

	//构造查询条件
    /**
    *	root	:Root接口,代表查询的根对象,可以通过root获取实体中的属性
    *	query	:代表一个顶层查询对象,用来自定义查询
    *	cb		:用来构建查询,此对象里有很多条件方法
    **/
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

6.3 findOne

6.3.1 根据条件,查询单个对象

    /**
     * 根据条件,查询单个对象
     *
     */
    @Test
    public void testSpec() {
        //匿名内部类
        /**
         * 自定义查询条件
         *      1.实现Specification接口(提供泛型:查询的对象类型)
         *      2.实现toPredicate方法(构造查询条件)
         *      3.需要借助方法参数中的两个参数(
         *          root:获取需要查询的对象属性
         *          CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
         *       )
         *  案例:根据客户名称查询,查询客户名为科技公司的客户
         *          查询条件
         *              1.查询方式
         *                  cb对象
         *              2.比较的属性名称
         *                  root对象
         *
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //1.获取比较的属性
                Path<Object> custName = root.get("custId");
                //2.构造查询条件  :    select * from cst_customer where cust_name = '科技公司'
                /**
                 * 第一个参数:需要比较的属性(path对象)
                 * 第二个参数:当前需要比较的取值
                 */
                Predicate predicate = cb.equal(custName, "程序员");//进行精准的匹配  (比较的属性,比较的属性的取值)
                return predicate;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

6.3.2 多条件查询

    /**
     * 多条件查询
     *      案例:根据客户名(科技公司)和客户所属行业查询(it教育)
     *
     */
    @Test
    public void testSpec1() {
        /**
         *  root:获取属性
         *      客户名
         *      所属行业
         *  cb:构造查询
         *      1.构造客户名的精准匹配查询
         *      2.构造所属行业的精准匹配查询
         *      3.将以上两个查询联系起来
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> custName = root.get("custName");//客户名
                Path<Object> custIndustry = root.get("custIndustry");//所属行业

                //构造查询
                //1.构造客户名的精准匹配查询
                Predicate p1 = cb.equal(custName, "程序员");//第一个参数,path(属性),第二个参数,属性的取值
                //2..构造所属行业的精准匹配查询
                Predicate p2 = cb.equal(custIndustry, "it");
                //3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系,满足条件一或满足条件二即可:或关系)
                Predicate and = cb.and(p1, p2);//以与的形式拼接多个查询条件
                // cb.or();//以或的形式拼接多个查询条件
                return and;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

示例

//  select * from user where id='c7825c1acede475fa04bc7189929e6ce' and dept='研发部' and (name='zhangsan' OR name='lisi')
List<User> users = userDao.findAll(new Specification<User>() {
    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

        return criteriaBuilder.and(
                criteriaBuilder.equal(root.get("id"),id),
                criteriaBuilder.equal(root.get("dept"),"研发部"),
                criteriaBuilder.or(
                    criteriaBuilder.equal(root.get("name"),"zhangsan"),
                    criteriaBuilder.equal(root.get("name"),"lisi")
                )
        );
    }
});

6.3.3 in查询

        List<Customer> customers = CustomerDao.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Spbussprocess> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<String> strings = spbusses.stream().map(Spbuss::getId).collect(Collectors.toList());
                return root.get("id").in(strings);
            }
        });

6.3.4 排序

List<User> users= userDao.findAll(new Specification<User>() {
    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    //按照cretime字段和updtime字段排序
        query.orderBy(criteriaBuilder.desc(root.get("age")), criteriaBuilder.desc(root.get("dept")));
        return criteriaBuilder.and(
            criteriaBuilder.equal(root.get("name"), "zhangsan"),
            criteriaBuilder.equal(root.get("age"), "20")
        );
    }
});

6.4 findAll

6.4.1 查询所有

    /**
     * 案例:完成根据客户名称的模糊匹配,返回客户列表
     *      客户名称以 ’科技公司‘ 开头
     *
     * equal :直接的到path对象(属性),然后进行比较即可
     * gt,lt,ge,le,like : 得到path对象,根据path指定比较的参数类型,再去进行比较
     *      指定参数类型:path.as(类型的字节码对象)
     */
    @Test
    public void testSpec3() {
        //构造查询条件
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //查询属性:客户名
                Path<Object> custName = root.get("custName");
                //查询方式:模糊匹配
                Predicate like = cb.like(custName.as(String.class), "科技公司%");
                return like;
            }
        };
//        List<Customer> list = customerDao.findAll(spec);
//        for (Customer customer : list) {
//            System.out.println(customer);
//        }
        //添加排序
        //创建排序对象,需要调用构造方法实例化sort对象
        //第一个参数:排序的顺序(倒序,正序)
        //   Sort.Direction.DESC:倒序
        //   Sort.Direction.ASC : 升序
        //第二个参数:排序的属性名称
        Sort sort = new Sort(Sort.Direction.DESC,"custId");
        List<Customer> list = customerDao.findAll(spec, sort);
        for (Customer customer : list) {
            System.out.println(customer);
        }
    }

6.4.2 分页查询所有

    /**
     * 分页查询
     *      Specification: 查询条件
     *      Pageable:分页参数
     *          分页参数:查询的页码,每页查询的条数
     *          findAll(Specification,Pageable):带有条件的分页
     *          findAll(Pageable):没有条件的分页
     *  返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,共条数)
     */
    @Test
    public void testSpec4() {

        Specification spec = null;
        //PageRequest对象是Pageable接口的实现类
        /**
         * 创建PageRequest的过程中,需要调用他的构造方法传入两个参数
         *      第一个参数:当前查询的页数(从0开始)
         *      第二个参数:每页查询的数量
         */
        Pageable pageable = new PageRequest(0,2);
        //分页查询
        Page<Customer> page = customerDao.findAll(null, pageable);
        System.out.println(page.getContent()); //得到数据集合列表
        System.out.println(page.getTotalElements());//得到总条数
        System.out.println(page.getTotalPages());//得到总页数
    }

6.5 方法对应关系

方法名称Sql对应关系
equlefiled = value
gt(greaterThan )filed > value
lt(lessThan )filed < value
ge(greaterThanOrEqualTo )filed >= value
le( lessThanOrEqualTo)filed <= value
notEqulefiled != value
likefiled like value
notLikefiled not like value

7、ExampleMatcher查询

        Xm xm=new Xm();
        xm.setId("025e47b62d144e869ee2e9e3112321395cd702");
        xm.setXmjd("111");
        //多条件and匹配
        ExampleMatcher exampleMatcher=ExampleMatcher.matchingAll();		//matchingAll全匹配,有几个参数,都要满足and
        //Example.of(查询条件,匹配规则)
        Example<Xm> example=Example.of(xm,exampleMatcher);
        List<Xm> xms= xmDao.findAll(example);

		//一样效果
/*        List<Xm> xms = xmDao.findAll(new Example<Xm>() {
            @Override
            public Xm getProbe() {  //查询参数
                Xm xm=new Xm();
                xm.setId("025e47b62d144e869ee2e9e3195cd702");
                xm.setXmjd("111");
                return xm;
            }

            @Override
            public ExampleMatcher getMatcher() {    //匹配规则
                return ExampleMatcher.matchingAll();    //matchingAll全匹配,有几个参数,都要满足and     id=? and xmjd=?
            }
        });*/        

匹配规则

方法名称Sql对应关系
exact=
startsWith开头模糊查询匹配 as%
startsWith结束模糊查询匹配 %as
contains模糊查询 %as%
//条件匹配
//withMatcher("参数",matcher -> matcher.匹配规则())
ExampleMatcher.matching().withMatcher("id",matcher -> matcher.exact()).withMatcher("xmjd",matcher -> matcher.exact());

参考:https://cloud.tencent.com/developer/article/1976451?from=15425&areaSource=102001.1&traceId=KGV_x7oBNBQbjInFSDMZH

https://jiuaidu.com/jianzhan/667681/

8、JPA的事务

JPA控制事务一般使用注解:@Transactional(rollbackFor = Exception.class)
但是方法太多了,就要写很多注解,很乱,而且不好管理,使用配置类:
需要用到execution表达式,资料:https://blog.csdn.net/thinkingcao/article/details/84872610

import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.Properties;

@Configuration
@EnableTransactionManagement
public class AspectjTransactionConfig {
    //  * com.wzw..*(..)     所有类型的方法,com.wzw.包以及com.wzw.的子包下的所有方法
    // * com.wzw.service.*(..)    com.wzw.service下的所有方法
    public static final String transactionExecution = "execution (* com.wzw..*(..))";
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        //指定一般要拦截哪些类
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(transactionExecution);
        //配置advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        //指定不同的方法用不通的策略
        Properties attributes = new Properties();
        attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
        attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
        //创建Interceptor
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);
        advisor.setAdvice(txAdvice);
        return advisor;
    }
}


可以使用 @Transactional 来覆盖全局的配置
transactionAttributes属性:https://www.cnblogs.com/peterkang202/p/6045832.html

9、常用注解

1、Entity 表示这个类是一个实体类,对应数据库中的一个表
2、@Table 指定这个类对应数据库中的表名。如果这个类名的命名方式符合数据库的命名方式,可以省略这个注解。如FlowType类名对应表名flow_type。
3、@Id 指定这个字段为表的主键
4、@GeneratedValue(strategy=GenerationType.IDENTITY) 指定主键的生成方式,一般主键为自增的话,就采用GenerationType.IDENTITY的生成方式
5、@Column(updatable = false) @Columun 注解针对一个字段,对应表中的一列。有很多参数,name表示对应数据表中的字段名。insertable 表示插入式是否更新。updateable,表示update的时候是否更新;columnDefinition表示字段类型,当使用jpa自动生成表的时候比较有用。
6、@PrePersist 表示持久化之前执行
7、@PreUpdate 表示执行Update操作之前执行。
8、SQLDelete表示当执行jpa中的delete操作时,执行的语句
9、@Where 当执行查询语句时,会附带这个条件。这个和上面的一起使用,可以实现软删除

10、其它

10.1 自动生成主键

    @Id
    @GeneratedValue(generator="system_uuid")
    @GenericGenerator(name="system_uuid",strategy="uuid")
    /**
     * 主键
     */
    @Column(name="id")
    private String id;

10.2 save方法自动填充修改和创建时间

  1. 启动类添加@EnableJpaAuditing注解
  2. 对应的实体类添加@EntityListeners(AuditingEntityListener.class)注解,对应字段添加@CreatedDate(创建时间)和@LastModifiedDate(修改时间)

10.2.1 修改的时候,@CreatedDate值为空了

使用属性updatable

    /**
     * 创建时间
     */
    @Column(name="cretime",updatable = false)
    @CreatedDate
    private Date cretime;

@Column属性:

参数类型描述
nameString列的名称,默认为属性的名称(Hibernate 映射列时,若遇到驼峰拼写,会自动添加 _ 连接并将大写字母改成小写)。
uniqueboolean列的值是否是唯一的。这是 @UniqueConstraint 注解的一个快捷方式, 实质上是在声明唯一约束。默认值为 false。
nullableboolean列的值是否允许为 null。默认为 true。
insertableboolean列是否包含在 INSERT 语句中,默认为 true。
updatableboolean列是否包含在 UPDATE 语句中,默认为 true。
columnDefinitionString生成列的 DDL 时使用的 SQL 片段。默认使用推断的类型来生成 SQL 片段以创建此列。
tableString当前列所属的表的名称。
lengthint列的长度,仅对字符串类型的列生效。默认为255。
precisionint列的精度,仅对十进制数值有效,表示有效数值的总位数。默认为0。
scaleint列的精度,仅对十进制数值有效,表示小数位的总位数。默认为0。

10.3 @Query传参的两种方式

  1. 位置参数
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
  1. 命名参数
@Query("select u from User u where u.emailAddress = :emailAddress")
User findByEmailAddress(@Param("emailAddress") String emailAddress);

10.4 直接保存为json对象到数据库

实体类中对应属性使用两个注解

@Type(type = "json")
//数据库对应字段名,数据库字段格式为文本格式,text,json,varchar
@Column(name = "users",columnDefinition = "json")
private List<User> users;

10.5 修改dao查询到的对象属性,没有做保存操作,数据库的值也被修改了

网上解答:

JPA有如上所示的四个生命周期:
New:瞬时对象,尚未有id,还未和Persistence Context建立关联的对象。
Managed:持久化受管对象,有id值,已经和Persistence Context建立了关联的对象。
Datached:游离态离线对象,有id值,但没有和Persistence Context建立关联的对象。
Removed:删除的对象,有id值,尚且和Persistence Context有关联,但是已经准备好从数据库中删除
当从数据库获取的数据后,因为有事务管理,所以数据已与session关联,并且数据库有数据,已经持久化了,并且在数据库的缓存当中了,当我们对查询出来的数据进行修改时,缓存缓存Session中的数据发生改变,那么接着数据库也会跟着进行相应的改变。所以就自动执行了update的更新操作。

解决办法:
一、new一个新的对象,拷贝属性给新对象,操作新对象的值,

BeanUtils.copyProperties(原对象,新对象);

二、如果使用的是EntityManager,有个entityManager.clear();方法,clear就好

11、分页

方法命名方式

dao层

@Repository
public interface UserDao extends GenericJpaRepository<User, String>, JpaSpecificationExecutor<User> {

    Page<User> findByName(String name, Pageable pageable);

}

service层

    @RequestMapping("/test")
    public Page<User> getUsers() {

        Pageable pageable = PageRequest.of(1, 6);

        Page<User> results = userDao.findByName("zhangsan", pageable);

        return results;

    }

Specifications动态查询

    @RequestMapping("/test")
    public Page<User> getSpecificationsByName() {

        Specification<User> specification = new Specification<>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("name"), "zhangsan");
            }
        };
		//参数1,第几页,参数二,每页条数
        Pageable pageable = PageRequest.of(1, 6);

        Page<User> results = UserDao.findAll(specification,pageable);
		//简写
        Page<User> results1 = UserDao.findAll((r,q,c)->c.equal(r.get("name"), "zhangsan"),pageable);
        

        return results;

    }

报错

Inferred type ‘S’ for type parameter ‘S’ is not within its bound; should ext

确定接口中的类是否正确
public interface PtBusinessLogsDao extends JpaRepository<PtBusinessLogs,String>, JpaSpecificationExecutor<PtBusinessLogs> {

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值