SpringData

Spring Data

源代码提取码:qjc8

1:Spring Data概述

1.1:Spring Data

  • Spring Data : Spring的一个子项目。用于简化数据库访问,支持NoSQL和关系数据存储。其主要目标是使数据库的访问变得方便快捷

  • SpringData 项目所支持NoSQL存储:

    • MongoDB (文档数据库)
    • Neo4j (图形数据库)
    • Redis (键/值存储)
    • Hbase (列族数据库)
  • SpringData项目所支持的关系数据存储技术:

    • JDBC
    • JPA

1.2:JPA Spring Data概述

  • JPA Spring Data :致力于减少数据访问层(DAO)的开发量.开发者唯一要做的,就只是声明持久层的接口,其他都交给Spring Data JPA来帮你完成
  • 框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个UserDao.findUserById0这样-个方法声明,大致应该能判断出这是根据给定条件的ID查询出满足条件的User对象。Spring Data JPA做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑

2:SpringData JPA HelloWorld

2.1:使用Spring Data JPA进行持久层开发需要的四个步骤

  • 配置Spring整合JPA
  • 在Spring配置文件中配置Spring Data ,让Spring为声明的接口创建代理对象。配置了jpa:repositories 后, Spring初始化容器时将会扫描base-package指定的包目录及其子目录,为继承Repository或其子接口的接口创建代理对象,并将代理对象注册为Spring Bean ,业务层便可以通过Spring自动封装的特性来直接使用该对象。
  • 声明持久层的接口,该接口继承Repository , Repository是一
    个标记型接口,它不包含任何方法,如必要, Spring Data可实现Repository其他子接口,中定义了一些常用的增删改查,以及分页相关的方法。
  • 在接口中声明需要的方法。Spring Data将根据给定的策略来为其生成实现代码

2.2:HelloWorld

  • 创建一个Java工程,创建lib,加入jar包 jar包提取码:smya
    在这里插入图片描述

  • 创建数据库,编写数据库文件,后面需要使用。名称:db.properties

    jdbc.user=root
    jdbc.password=123456
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql:///jpaNew
    
  • src目录下创建applicationContext.xml文件

    • 配置数据源

      <?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:context="http://www.springframework.org/schema/context"
      	xmlns:tx="http://www.springframework.org/schema/tx"
      	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
      	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc 
      		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd 
      		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
      		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
      		
      		<!-- 1. 配置数据源 -->
      		<context:property-placeholder location="classpath:db.properties"/>
      		
      		<bean id="dataSource" 
      			class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
      			<property name="user" value="${jdbc.user}"></property>
      			<property name="password" value="${jdbc.password}"></property>
      			<property name="driverClass" value="${jdbc.driverClass}"></property>
      			<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
      			
      			<!--配置其他属性 -->
      		</bean>
      		
      		<!-- 2. 配置JPA的EntityManagerFactory -->
      		
      		<!-- 3. 配置事务管理器 -->
      		
      		<!-- 1. 配置支持注解的事物 -->
      		
      		<!-- 1. 配置springData -->
      		
      </beans>
      
      • 编写测试代码进行测试确保数据源可以正常使用

        package com.zdww.springdata.test;
        
        import java.sql.SQLException;
        
        import javax.sql.DataSource;
        
        import org.junit.Test;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        public class SpringDataTest {
        	
        	private static ApplicationContext ctx=null;
        	
        	{
        		ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        	}
        
        	@Test
        	public void testDataSource() throws SQLException {
        		DataSource dataSource=ctx.getBean(DataSource.class);
        		System.out.println(dataSource.getConnection());
        	}
        	
        }
        
        
      • 测试结果

        com.mchange.v2.c3p0.impl.NewProxyConnection@482cd91f
        
    • 配置JPA的EntityManagerFactory

      <!-- 2. 配置JPA的EntityManagerFactory -->
      		<bean id="entityManagerFactory" 
      			class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      			<property name="dataSource" ref="dataSource"></property>
      			<property name="jpaVendorAdapter">
      				<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
      			</property>
      			<property name="packagesToScan" value="com.zdww.springdata"></property>
      			<property name="jpaProperties">
      				<props>
      					<!-- 生成的数据表的列的映射策略 -->
      					<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
      					<!-- hibernate基本属性 -->
      					<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
      					<prop key="hibernate.format_sql">true</prop>
      					<prop key="hibernate.show_sql">true</prop>
      					<prop key="hibernate.hbm2ddl.auto">update</prop>
      				</props>
      			</property>
      		</bean>
      
      • 编写实体类并测试

        package com.zdww.springdata;
        
        import java.sql.Date;
        
        import javax.persistence.Entity;
        import javax.persistence.GeneratedValue;
        import javax.persistence.Id;
        import javax.persistence.Table;
        
        @Table(name="JPA_PERSONS")
        @Entity
        public class Person {
        	
        	private Integer id;
        	private String lastName;
        	
        	private String email;
        	private Date birth;
        	
        	@GeneratedValue
        	@Id
        	public Integer getId() {
        		return id;
        	}
        	public void setId(Integer id) {
        		this.id = id;
        	}
        	public String getLastName() {
        		return lastName;
        	}
        	public void setLastName(String lastName) {
        		this.lastName = lastName;
        	}
        	public String getEmail() {
        		return email;
        	}
        	public void setEmail(String email) {
        		this.email = email;
        	}
        	public Date getBirth() {
        		return birth;
        	}
        	public void setBirth(Date birth) {
        		this.birth = birth;
        	}
        
        }
        
        
      • 测试(由于前部分:ctx=new ClassPathXmlApplicationContext("applicationContext.xml,所以自动创建数据表)若正常生成数据表,则表示配置成功;

        	@Test
        	public void testJpa() {
        		
        	}
        
    • 配置事务管理器,配置支持注解的事物

      • <!-- 3. 配置事务管理器 -->
        		<bean id="transactionManager" 
        			class="org.springframework.orm.jpa.JpaTransactionManager">
        			<property name="entityManagerFactory" ref="entityManagerFactory"></property>
        			
        		</bean>
        		<!-- 4. 配置支持注解的事物 -->
        		<tx:annotation-driven transaction-manager="transactionManager"/>
        
    • 配置springData

      • 加入jar包(spring-data-commons;spring-data-jpa;slf4j-api;2.2已包含),配置springdata

        <!-- 5. 配置springData -->
        		<!-- 加入jpa的命名空间 -->
        		<!-- base-package:扫描Repository Bean所在的package -->
            	<jpa:repositories base-package="com.zdww.springdata"
        			transaction-manager-ref="transactionManager"
        			entity-manager-factory-ref="entityManagerFactory" />
        
      • 创建PersonRepsotory接口,继承Repository

        package com.zdww.springdata;
        
        import org.springframework.data.repository.Repository;
        
        public interface PersonRepsotory extends Repository<Person, Integer>{
        	
        	//根据lastName来获取对应的Person
        	Person getByLastName(String lastName);
        
        }
        
        
      • 生成实体类Person的toString方法

        @Override
        	public String toString() {
        		return "Person [id=" + id + ", lastName=" + lastName + ", email=" + email + ", birth=" + birth + "]";
        	}
        
      • 创建测试类,查询数据

        	@Test
        	public void testHelloWorldSpringData() {
        		PersonRepsotory personRepsotory=ctx.getBean(PersonRepsotory.class);
        		
        		Person person=personRepsotory.getByLastName("AA");
        		System.out.println(person);
        		
        	}
        
      • 结果

        Hibernate: 
            select
                person0_.id as id1_0_,
                person0_.birth as birth2_0_,
                person0_.email as email3_0_,
                person0_.last_name as last_nam4_0_ 
            from
                jpa_persons person0_ 
            where
                person0_.last_name=?
        Person [id=1, lastName=AA, email=aa@zdww.com, birth=2020-06-16]
        

3:Repository接口

3.1:Repository接口概述

  • Repository接口是Spring Data的一个核心接口,它不提供任何方法,发者需要在自己定义的接口中声明需要的方法 public interface PersonRepsotory extends Repository<Person, Integer>

  • Spring Data可以让我们只定义接口,只要遵循Spring Data的规范,无需写实现类

  • 若我们定义的接口继承了Repository,则该接口会被IOC容器识别为一个Repository Bean,纳入到IOC容器中,进而可以在改接口中定义满足一定规范的方法.

  • 与继承Repository等价的一种方式,就是在持久层接口上使用**@RepositoryDefinition**注解,并为其指定domainClass和idClass属性

    • @RepositoryDefinition(domainClass=Person.class,idClass=Integer.class)
      
  • 支持属性的级联查询.若当前类有符合条件的属性,则优先使用,而不使用级联属性

  • 若需要使用级联属性,则属性之间使用_连接

3.2:Repository子接口

  • 基础的Repository提供了最基本的数据访问功能其几个子接口则扩展了一些功能。它们的继承关系如下:
    • Repository:仅仅是一个标识 ,表明任何继承它的均为仓库接口类
    • CrudRepository:继承Repository ,实现了一组CRUD相关的方法
    • PagingAndSortingRepository:继承CrudRepository,实现了一组分页排序相关的方法,但不能实现带条件查询的分页排序
    • JpaRepository :继承PagingAndSortingRepository ,实现一组JPA规范相关的方法
    • 自定义的XxxxRepository需要继承JpaRepository ,这样的XxxxRepository接口就具备了通用的数据访问控制层的能力
    • JpaSpecificationExecutor:不属于Repository体系 ,实现一组 JPA Criteria查询相关的方法。实现带条件查询的分页排序
      在这里插入图片描述

4:SpringData方法定义规范

4.1:简单条件查询

  • 声明需要符合一定的规范
  • 查询方法以find|read|get开头
  • 涉及条件查询时,条件的属性用条件关键字连
  • 要注意的是:条件属性以首字母大写

4.2:支持的关键字

KeywordSampleJPQL snippet
AndfindByLastnameAndFirstnamewhere x.lastname = ?1 and x.firstname= ?2
OrfindByLastnameOrFirstnamewhere x.lastname = ?1 or x.firstname= ?2
BetweenfindByStartDateBetweenwhere x.startDate between 1? and ?2
LessThanfindByAgeLessThanwhere x.age <?1
GreaterThanfindByAgeGreaterThanwhere x.age>?1
AfterfindByStatDateAfterwhere x.startDate>?1
BeforefindByStartDateBeforewhere x.startDate <?1
isNullfindByAgelsNullwhere x.age is null
isNotNull,NotNullfindByAge(Is)NotNullwhere x.age not null
LikefindByFirstnameLikewhere x.firstname like ?1
NotLikefindByFirstNameNotLikewhere x.frstname not like ?1
StartingWithfindByFirstnameStartingWithWhere x.frstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWithwhere x.frsname ike ?1 (panameter bound with prepended %)
ContainingfindByFirstnameContainingWhere x.frstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDescwhere x.age = ?1 order by x.lastname desc
NotfindByLastnameNotwherex.lastname<>?1
InfindByAgeIn(Collection ages)where x.age in ?1
NotInfindByAgeNotIn(Collclion age)where x.age not in?1
TRUEfindByActiveTrue0where x.actve = true
FALSEfindByActiveFalse0where x.active = fals

4.3:测试

  • WHERE lastNmae LIKE ?% AND id < ? (StartingWith)

    • PersonRepository接口

      //WHERE lastNmae LIKE ?% AND id < ?
      List<Person> getByLastNameStartingWithAndIdLessThan(String lastName,Integer id);
      
    • 测试类代码

      package com.zdww.springdata.test;
      
      import java.sql.SQLException;
      import java.util.Arrays;
      import java.util.Date;
      import java.util.List;
      
      import javax.sql.DataSource;
      
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      import com.zdww.springdata.Person;
      import com.zdww.springdata.PersonRepsotory;
      import com.zdww.springdata.PersonService;
      
      public class SpringDataTest {
      	
      	private static ApplicationContext ctx=null;
      	private PersonRepsotory personRepsotory=null;
      	
      	{
      		ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
      		personRepsotory=ctx.getBean(PersonRepsotory.class);
      	}
          
          @Test
      	public void testKeyWords() {
      		List<Person> persons=personRepsotory.getByLastNameStartingWithAndIdLessThan("F", 10);
      		System.out.println(persons);
      		
         	 	}
      	}
          
      
    • 测试结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              (
                  person0_.last_name like ?
              ) 
              and person0_.id<?
      [Person [id=5, lastName=FF, email=ff@zdww.com, birth=2020-06-03, address=com.zdww.springdata.Address@27494e46]]
      
  • //WHERE lastNmae LIKE %? AND id < ?(EndingWith)

    • PersonRepository接口

      //WHERE lastNmae LIKE %? AND id < ?
      List<Person> getByLastNameEndingWithAndIdLessThan(String lastName,Integer id);
      
    • 测试类代码

      	@Test
      	public void testKeyWords() {
      
      		List<Person> persons1=personRepsotory.getByLastNameEndingWithAndIdLessThan("F", 10);
      		System.out.println(persons1);
      	}
          
      
    • 测试结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              (
                  person0_.last_name like ?
              ) 
              and person0_.id<?
      [Person [id=4, lastName=GF, email=gg@zdww.com, birth=2020-06-08, address=com.zdww.springdata.Address@d59970a], Person [id=5, lastName=FF, email=ff@zdww.com, birth=2020-06-03, address=com.zdww.springdata.Address@6a9287b1]]
      
      
  • //WHERE email IN(?,?,?) OR birth < ?(In)

    • PersonRepository接口

      //WHERE email IN(?,?,?) OR birth < ?
      List<Person> getByEmailInAndBirthLessThan(List<String> emails,Date date);
      
    • 测试类代码

      	@Test
      	public void testKeyWords() {
      		List<Person> persons2=personRepsotory.getByEmailInAndBirthLessThan(Arrays.asList("aa@zdww.com","dd@zdww.com","gg@zdww.com"), new Date());
      		System.out.println(persons2.size());
      	}
          
      
    • 测试结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              (
                  person0_.email in (
                      ? , ? , ?
                  )
              ) 
              and person0_.birth<?
      2
      
      
      

4.4:存在的一个问题

  • 增加实体类Address

    package com.zdww.springdata;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Table(name="JPA_ADDRESS")
    @Entity
    public class Address {
    	
    	private Integer id;
    	private String province;
    	private String city;
    	
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	public String getProvince() {
    		return province;
    	}
    	public void setProvince(String province) {
    		this.province = province;
    	}
    	public String getCity() {
    		return city;
    	}
    	public void setCity(String city) {
    		this.city = city;
    	}
    
    }
    
    
  • 在Person中加入Address,运行,生成Address数据表

    	private Address address;
    
    	@JoinColumn(name="ADDRESS_ID")
    	@ManyToOne
    	public Address getAddress() {
    		return address;
    	}
    	public void setAddress(Address address) {
    		this.address = address;
    	}
    
  • 写一个接口:WHERE a.id > ?

    //WHERE a.id > ?
    List<Person> getByAddress_IdGreaterThan(Integer id);
    
  • 测试类

    	@Test
    	public void testKey2() {
    		List<Person> persons=personRepsotory.getByAddress_IdGreaterThan(1);
    		System.out.println(persons);
    	}
    
  • 结果

    Hibernate: 
        select
            person0_.id as id1_1_,
            person0_.address_id as address_5_1_,
            person0_.birth as birth2_1_,
            person0_.email as email3_1_,
            person0_.last_name as last_nam4_1_ 
        from
            jpa_persons person0_ 
        left outer join
            jpa_address address1_ 
                on person0_.address_id=address1_.id 
        where
            address1_.id>?
    [Person [id=1, lastName=AA, email=aa@zdww.com, birth=2020-06-16, address=com.zdww.springdata.Address@53b98ff6], Person [id=3, lastName=DD, email=lbs@zdww.com, birth=2020-06-16, address=com.zdww.springdata.Address@6c8a68c1], Person]
    
    
  • 运行成功,说明支持级联属性

  • 如果在Person加入以下属性和方法,

    	private Integer addressId;
    
    	@Column(name="ADD_ID")
    	public Integer getAddressId() {
    		return addressId;
    	}
    	public void setAddressId(int addressId) {
    		this.addressId = addressId;
    	}
    
  • 结果

    Hibernate: 
        select
            person0_.id as id1_1_,
            person0_.address_id as address_6_1_,
            person0_.add_id as add_id2_1_,
            person0_.birth as birth3_1_,
            person0_.email as email4_1_,
            person0_.last_name as last_nam5_1_ 
        from
            jpa_persons person0_ 
        where
            person0_.add_id>?
    [Person [id=2, lastName=BB, email=bb@zdww.com, birth=2020-06-09, address=com.zdww.springdata.Address@452c8a40], Person [id=3, lastName=DD, email=lbs@zdww.com, birth=2020-06-16, address=com.zdww.springdata.Address@697446d4]]
    
    
  • 说明查询的是private Integer addressId属性,即就近原则

  • 如果在这种情况下需要获取级联属性,则声明方法时使用 _ 连接

    //WHERE a.id > ?
    List<Person> getByAddress_IdGreaterThan(Integer id);
    

5:使用@Query注解

5.1:@Query概述

  • 使用@query注解可以自定义JPQL语句以实现更灵活的查询
  • 为@query注解传递参数可使用占位符
  • 为@query注解传递参数:命名参数的方式
  • SpringData允许在占位符上添加%%.或者调用方法的参数上加%%
  • 设置nativeQuery=true 即可以使用原生SQL

5.2:测试

  • 查询id最大的那个Person

    • PersonRepository接口
    //查询id最大的那个Person
    //使用@query注解可以自定义JPQL语句以实现更灵活的查询
    @Query("SELECT p FROM Person p WHERE p.id=(SELECT max(p2.id) FROM Person p2)")
    Person getMaxIdPerson();
    
    • 测试代码

      	@Test
      	public void testQueryAnnotation() {
      		Person persons=personRepsotory.getMaxIdPerson();
      		System.out.println(persons);
      	}
      
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              person0_.id=(
                  select
                      max(person1_.id) 
                  from
                      jpa_persons person1_
              )
      Person [id=8, lastName=VV, email=vv@zdww.com, birth=2020-06-04, address=com.zdww.springdata.Address@1059754c]
      
      
  • 传递参数-1 使用占位符

    • PersonRepository接口
    //为@query注解传递参数的方式1:使用占位符
    @Query("SELECT p FROM Person p WHERE p.lastName= ?1 AND p.email= ?2")
    List<Person> testQueryAnnotationParams1(String lastName,String email);
    
    • 测试代码

      	@Test
      	public void testQueryAnnotationParams1() {
      		List<Person> persons=personRepsotory.testQueryAnnotationParams1("DD", "dd@zdww.com");
      		System.out.println(persons);
      	}
      
      
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              person0_.last_name=? 
              and person0_.email=?
      []
      
      
  • 传递参数-2 命名参数的方式

    • PersonRepository接口
    //为@query注解传递参数的方式2:命名参数的方式
    @Query("SELECT p FROM Person p WHERE p.lastName= :lastName AND p.email= :email")
    List<Person> testQueryAnnotationParams2(@Param("email") String email,@Param("lastName") String lastName);
    
    • 测试代码

      	@Test
      	public void testQueryAnnotationParams2() {
      		List<Person> persons=personRepsotory.testQueryAnnotationParams2("dd@zdww.com","DD");
      		System.out.println(persons);
      	}
      
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              person0_.last_name=? 
              and person0_.email=?
      []
      
      
      
  • 传递参数-3 SpringData允许在占位符上添加%%.或者调用方法的参数上加%%.

    • PersonRepository接口
    @Query("SELECT p FROM Person p WHERE p.lastName LIKE %?1% OR p.email LIKE %?2%")
    List<Person> testQueryAnnotationLikeParams(String lastName,String email);
    
    • 测试代码

      	@Test
      	public void testQueryAnnotationLikeParams() {
      //		List<Person> persons=personRepsotory.testQueryAnnotationLikeParams("%F%", "%dd%");
      //		System.out.println(persons);
      		
      		List<Person> persons=personRepsotory.testQueryAnnotationLikeParams("F", "dd");
      		System.out.println(persons);
      	}
      
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              person0_.last_name like ? 
              or person0_.email like ?
      [Person [id=4, lastName=GF, email=gg@zdww.com, birth=2020-06-08, address=com.zdww.springdata.Address@4925f4f5], Person [id=5, lastName=FF, email=ff@zdww.com, birth=2020-06-03, address=com.zdww.springdata.Address@5339bbad]]
      
      
  • 传递参数-4 为@query注解传递参数like的方式2:命名参数的方式

    • PersonRepository接口
    @Query("SELECT p FROM Person p WHERE p.lastName LIKE %:lastName% OR p.email LIKE %:email%")
    List<Person> testQueryAnnotationLikeParams2(@Param("email") String email,@Param("lastName") String lastName);
    
    • 测试代码

      	@Test
      	public void testQueryAnnotationLikeParams() {
      		List<Person> persons=personRepsotory.testQueryAnnotationLikeParams2("dd","F");
      		System.out.println(persons);
      	}
      
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_,
              person0_.address_id as address_6_1_,
              person0_.add_id as add_id2_1_,
              person0_.birth as birth3_1_,
              person0_.email as email4_1_,
              person0_.last_name as last_nam5_1_ 
          from
              jpa_persons person0_ 
          where
              person0_.last_name like ? 
              or person0_.email like ?
      [Person [id=4, lastName=GF, email=gg@zdww.com, birth=2020-06-08, address=com.zdww.springdata.Address@4925f4f5], Person [id=5, lastName=FF, email=ff@zdww.com, birth=2020-06-03, address=com.zdww.springdata.Address@5339bbad]]
      
      
      
  • 传递参数-5 设置nativeQuery=true 即可以使用原生SQL

    • PersonRepository接口
    //设置nativeQuery=true 即可以使用原生SQL
    @Query(value="SELECT count(id) FROM jpa_persons",nativeQuery=true)
    long getTotalCount();
    
    • 测试代码

      	@Test
      	public void TotalCount() {
      		long count=personRepsotory.getTotalCount();
      		System.out.println(count);
      	}
      
    • 结果

      Hibernate: 
          SELECT
              count(id) 
          FROM
              jpa_persons
      8
      

6:@Modifying注解

6.1:@Modifying注解概述

  • 可以通过自定义的JPQL完成UPDATE 和 DELETE 操作。注意:JPQL不支持INSERT
  • 在@Query注解中,编写JPQL语句,但必须使用@Modifying进行修饰,以通知SpringData,这是一个UPDATE 或DELETE操作
  • UPDATE 和 DELETE 操作需要使用事务,此时需要定义Service层,在Service层的方法上添加事务
  • 默认情况下,SpringData的每个方法上都有事务,但都是一个只读事务,它们不能完成修改操作

6.2:测试

  • @Modifying注解

    • PersonRepository接口
    //可以通过自定义的JPQL完成UPDATE 和 DELETE 操作。注意:JPQL不支持INSERT
    //在@Query注解中,编写JPQL语句,但必须使用@Modifying进行修饰,以通知SpringData,这是一个UPDATE 或DELETE操作
    //UPDATE 和 DELETE 操作需要使用事务,此时需要定义Service层,在Service层的方法上添加事务
    //默认情况下,SpringData的每个方法上都有事务,但都是一个只读事务,它们不能完成修改操作
    @Modifying
    @Query("UPDATE Person p SET p.email = :email WHERE id = :id")
    void updatePersonEmail(@Param("id") Integer id,@Param("email") String email);
    
    • 创建Service以便于添加事务

      package com.zdww.springdata;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      
      @Service
      public class PersonService {
      	
      	@Autowired
      	private PersonRepsotory personRepsotory;
      	
      	@Transactional
      	public void updatePersonEmail(String email,Integer id) {
      		personRepsotory.updatePersonEmail(id, email);
      	}
      
      }
      
      
    • 测试代码

      public class SpringDataTest {
      	
      	private static ApplicationContext ctx=null;
      	private PersonRepsotory personRepsotory=null;
      	private PersonService personService=null;
      	
      	{
      		ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
      		personRepsotory=ctx.getBean(PersonRepsotory.class);
      		personService=ctx.getBean(PersonService.class);
      	}
      	
      	@Test
      	public void updatePersonEmail() {
      		personService.updatePersonEmail("lbs@zdww.com",3);
      	}
      }
      
    • 配置自动扫描的包

      <!-- 配置自动扫描的包 -->
      	<context:component-scan base-package="com.zdww.springdata"></context:component-scan>
      
    • 结果

      Hibernate: 
          update
              jpa_persons 
          set
              email=? 
          where
              id=?
      

7:CrudRepository子接口

在这里插入图片描述

  • 改为extends CrudRepository<Person, Integer>

  • 测试save,删除Person表数据

  • Service添加方法

    	@Transactional
    	public void savePersons(List<Person> persons) {
    		personRepsotory.save(persons);
    	}
    
  • 编写测试类方法保存数据

    	@Test
    	public void testCrudRepository() {
    		List<Person> persons=new ArrayList<>();
    		
    		for(int i='a';i<='z';i++) {
    			Person person=new Person();
    			person.setAddressId(i+1);
    			person.setBirth(new Date());
    			person.setEmail((char)i +""+(char)i+"@zdww.com");
    			person.setLastName((char)i+""+(char)i);
    			
    			persons.add(person);
    			
    		}
    		personService.savePersons(persons);
    	}
    
  • 结果
    在这里插入图片描述

8:PagingAndSortingRepository子接口

在这里插入图片描述

  • 改为extends PagingAndSortingRepository<Person, Integer>

  • 由于需要分页,把数据表id改为从1开始,方便查看分页效果

  • 测试
    在这里插入图片描述

    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    
    import javax.sql.DataSource;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.domain.Sort.Direction;
    import org.springframework.data.domain.Sort.Order;
    
    import com.zdww.springdata.Person;
    import com.zdww.springdata.PersonRepsotory;
    import com.zdww.springdata.PersonService;
    
    	@Test
    	public void testPagingAndSortingRepository() {
    		//pageNo从0开始
    		int pageNo=3-1;
    		int PageSize=5;
    		//Pageable接口通常使用的PageRequest实现类,其中封装了需要分页的信息
    		//排序相关
    		Order order1=new Order(Direction.DESC,"id");
    		Order order2=new Order(Direction.ASC,"email");
    		Sort sort=new Sort(order1,order2);
    		
    		PageRequest pageable=new PageRequest(pageNo, PageSize,sort);
    		Page<Person> page=personRepsotory.findAll(pageable);
    		
    		System.out.println("总记录数: "+page.getTotalElements());
    		System.out.println("当前第几页: "+(page.getNumber()+1);
    		System.out.println("总页数: "+page.getTotalPages());
    		System.out.println("当前页面的List: "+page.getContent());
    		System.out.println("当前页面的记录数: "+page.getNumberOfElements());
    				
    	}
    
  • 结果

    Hibernate: 
        select
            count(*) as col_0_0_ 
        from
            jpa_persons person0_
    Hibernate: 
        select
            person0_.id as id1_1_,
            person0_.address_id as address_6_1_,
            person0_.add_id as add_id2_1_,
            person0_.birth as birth3_1_,
            person0_.email as email4_1_,
            person0_.last_name as last_nam5_1_ 
        from
            jpa_persons person0_ 
        order by
            person0_.id desc,
            person0_.email asc limit ?,
            ?
    总记录数: 26
    当前第几页: 3
    总页数: 6
    当前页面的List: [Person [id=16, lastName=hh, email=hh@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=15, lastName=gg, email=gg@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=14, lastName=ff, email=ff@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=13, lastName=ee, email=ee@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=12, lastName=dd, email=dd@zdww.com, birth=2020-06-18 00:00:00.0, address=null]]
    当前页面的记录数: 5
    

9:JpaRepository子接口

在这里插入图片描述

  • 改为extends JpaRepository<Person, Integer>

  • 测试-1(没有id)

    	@Test
    	public void testJpaRepository() {
    
    			Person person=new Person();
    			person.setBirth(new Date());
    			person.setEmail("ls@zdww.com");
    			person.setLastName("ls");
    			
    			personRepsotory.saveAndFlush(person);
    	}
    
    • 结果

      Hibernate: 
          insert 
          into
              jpa_persons
              (address_id, add_id, birth, email, last_name) 
          values
              (?, ?, ?, ?, ?)
      
  • 测试-2(id,相当于jpa的merage方法)

    	@Test
    	public void testJpaRepository() {
    
    			Person person=new Person();
    			person.setBirth(new Date());
    			person.setEmail("ls@zdww.com");
    			person.setLastName("ls");
    			person.setId(114);
    			
    			Person person2=personRepsotory.saveAndFlush(person);
    			System.out.println(person==person2);
    	}
    
    • 结果

      Hibernate: 
          select
              person0_.id as id1_1_0_,
              person0_.address_id as address_6_1_0_,
              person0_.add_id as add_id2_1_0_,
              person0_.birth as birth3_1_0_,
              person0_.email as email4_1_0_,
              person0_.last_name as last_nam5_1_0_ 
          from
              jpa_persons person0_ 
          where
              person0_.id=?
      Hibernate: 
          update
              jpa_persons 
          set
              address_id=?,
              add_id=?,
              birth=?,
              email=?,
              last_name=? 
          where
              id=?
      false
      

10:JpaSpecificationExecutor接口

10.1:JpaSpecificationExecutor概述

  • 不属于Repository体系 ,实现一组JPA Criteria查询相关的方法
    在这里插入图片描述
  • Specification :封装JPA Criteria查询条件。通常使用匿名内部类的方式来创建该接口的对象

10.2:测试

  • 添加extends JpaRepository<Person, Integer>,JpaSpecificationExecutor<Person>

  • 测试代码

    /*
    	 * 目标:实现带查询条件的分页   id>5
    	 * 调用  JpaSpecificationExecutor的Page<T> findAll(Specification<T> spec, Pageable pageable);
    	 * Specification 封装了Jpa Criteria查询的查询条件
    	 * Pageable:封装了请求分页的信息:例如pageNo,pageSize,sort
    	 */
    	@Test
    	public void testJpaSpecificationExecutor() {
    		int pageNo=3-1;
    		int PageSize=5;
    		PageRequest pageable=new PageRequest(pageNo, PageSize);
    		
    		//通常使用Specification的匿名内部类
    		Specification<Person> specification=new Specification<Person>() {
    			/*
    			 * @param *root:代表查询的实体类
    			 * @param query:可以从中得到Root对象,即告知Jpa Criteria 查询要查询哪一个实体类,还可以来添加查询条件,
    			 * 还可以结合EntityManager对象得到最终查询的TypedQuery对象
    			 * @param *cb: CriteriaBuilder 对象,用于创建Criteria相关对象的工厂,当然可以从中获取到Predicate对象
    			 * @return *Predicate类型,代表一个查询条件
    			 */
    			@Override
    			public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    				Path path=root.get("id");
    				Predicate predicate=cb.gt(path,5);
    				return predicate;
    			}
    		};
    		
    		Page<Person> page=personRepsotory.findAll(specification, pageable);
    		
    		System.out.println("总记录数: "+page.getTotalElements());
    		System.out.println("当前第几页: "+(page.getNumber()+1));
    		System.out.println("总页数: "+page.getTotalPages());
    		System.out.println("当前页面的List: "+page.getContent());
    		System.out.println("当前页面的记录数: "+page.getNumberOfElements());
    	}
    

在这里插入图片描述

  • 结果

    Hibernate: 
        select
            count(*) as col_0_0_ 
        from
            jpa_persons person0_ 
        where
            person0_.id>5
    Hibernate: 
        select
            person0_.id as id1_1_,
            person0_.address_id as address_6_1_,
            person0_.add_id as add_id2_1_,
            person0_.birth as birth3_1_,
            person0_.email as email4_1_,
            person0_.last_name as last_nam5_1_ 
        from
            jpa_persons person0_ 
        where
            person0_.id>5 limit ?, ?
    总记录数: 23
    当前第几页: 3
    总页数: 5
    当前页面的List: [Person [id=16, lastName=hh, email=hh@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=17, lastName=ii, email=ii@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=18, lastName=jj, email=jj@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=19, lastName=kk, email=kk@zdww.com, birth=2020-06-18 00:00:00.0, address=null], Person [id=20, lastName=ll, email=ll@zdww.com, birth=2020-06-18 00:00:00.0, address=null]]
    当前页面的记录数: 5
    

11:自定义Repository方法

11.1:为某一个Repository上添加自定义方法

  • 步骤:

    • 定义一一个接口:声明要添加的,并自实现的方法提供该接口的实现类:类名需在要声明的Repository后添加Impl,并实现方法

    • 声明Repository接口,并继承1)声明的接口

    • 注意:默认情况下,Spring Data会在base-package中查找"接口名Impl"作为实现类.也可以通过
      repository-impl-postfix声明后缀
      在这里插入图片描述

  • 首先创建一个接口 PersonDao

    package com.zdww.springdata;
    
    public interface PersonDao {
    
    	void test();
    }
    
  • 创建PersonDao的实现类PersonDaoImpl

    package com.zdww.springdata;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    public class PersonRepsotoryImpl implements PersonDao {
    
    	@PersistenceContext
    	private EntityManager entityManager;
    	
    	@Override
    	public void test() {
    		Person person=entityManager.find(Person.class, 11);
    		System.out.println("-->" + person);
    
    	}
    
    }
    
  • 在PersonRepsotory上继承自定义的接口

    public interface PersonRepsotory extends JpaRepository<Person, Integer>,JpaSpecificationExecutor<Person>,PersonDao
    
  • 写测试类

    	@Test
    	public void testCustomRepositoryMethod() {
    		personRepsotory.test();
    	}
    
  • 结果

    Hibernate: 
        select
            person0_.id as id1_1_1_,
            person0_.address_id as address_6_1_1_,
            person0_.add_id as add_id2_1_1_,
            person0_.birth as birth3_1_1_,
            person0_.email as email4_1_1_,
            person0_.last_name as last_nam5_1_1_,
            address1_.id as id1_0_0_,
            address1_.city as city2_0_0_,
            address1_.province as province3_0_0_ 
        from
            jpa_persons person0_ 
        left outer join
            jpa_address address1_ 
                on person0_.address_id=address1_.id 
        where
            person0_.id=?
    -->Person [id=11, lastName=cc, email=cc@zdww.com, birth=2020-06-18 00:00:00.0, address=null]
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值