文章目录
Spring Data
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:支持的关键字
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | where x.lastname = ?1 and x.firstname= ?2 |
Or | findByLastnameOrFirstname | where x.lastname = ?1 or x.firstname= ?2 |
Between | findByStartDateBetween | where x.startDate between 1? and ?2 |
LessThan | findByAgeLessThan | where x.age <?1 |
GreaterThan | findByAgeGreaterThan | where x.age>?1 |
After | findByStatDateAfter | where x.startDate>?1 |
Before | findByStartDateBefore | where x.startDate <?1 |
isNull | findByAgelsNull | where x.age is null |
isNotNull,NotNull | findByAge(Is)NotNull | where x.age not null |
Like | findByFirstnameLike | where x.firstname like ?1 |
NotLike | findByFirstNameNotLike | where x.frstname not like ?1 |
StartingWith | findByFirstnameStartingWith | Where x.frstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | where x.frsname ike ?1 (panameter bound with prepended %) |
Containing | findByFirstnameContaining | Where x.frstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | wherex.lastname<>?1 |
In | findByAgeIn(Collection ages) | where x.age in ?1 |
NotIn | findByAgeNotIn(Collclion age) | where x.age not in?1 |
TRUE | findByActiveTrue0 | where x.actve = true |
FALSE | findByActiveFalse0 | where 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]