一.Spring整合Spring Data JPA
Spring Data JPA是Spring Data项目下的一个模块。提供了一套基于JPA标准操作数据库的简化方案,底层默认是依赖Hibernate JPA来实现的。
Spring Data JPA的技术特点:我们只需要定义接口并继承Spring Data JPA中所提供的接口就可以了。不需要编写接口实现类。
1.创建Spring Data JPA的项目,导入依赖,编写配置文件
<dependencies> <!--Spring Ioc相关依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.3.18</version> </dependency> <!--Spring Aop的相关依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.8.RC2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.18</version> </dependency> <!--Spring jdbc的相关依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.18</version> </dependency> <!--spring orm的相关依赖--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.3.18</version> </dependency> <!--单元测试用的依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.18</version> </dependency> <!--日志依赖--> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <!--Hibernate的核心依赖,9个必须要导入--> <!-- https://mvnrepository.com/artifact/antlr/antlr --> <dependency> <groupId>antlr</groupId> <artifactId>antlr</artifactId> <version>2.7.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.geronimo.specs/geronimo-jta_1.1_spec --> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-jta_1.1_spec</artifactId> <version>1.1.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate.common/hibernate-commons-annotations --> <dependency> <groupId>org.hibernate.common</groupId> <artifactId>hibernate-commons-annotations</artifactId> <version>5.1.2.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.5.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.1-api --> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>1.0.0.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jboss/jandex --> <dependency> <groupId>org.jboss</groupId> <artifactId>jandex</artifactId> <version>2.0.0.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.javassist/javassist --> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jboss.logging/jboss-logging --> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.4.1.Final</version> </dependency> <!--mysql数据驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!--连接池相关的依赖--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-c3p0 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>5.6.7.Final</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>mchange-commons-java</artifactId> <version>0.2.19</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.6.7.Final</version> </dependency> <!--Spring Data JPA的相关依赖--> <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>2.6.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.6.3</version> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.28</version> </dependency> </dependencies>
applicationContext.xml相比之前spring整合Hibernate JPA时多了一个jpa的命名空间,其他也不用删除先,然后添加一个jpa的dao扫描,具体配置如下:
<?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:aop="http://www.springframework.org/schema/aop" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置读取properties文件的工具类--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置c3p0数据库连接池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="driverClass" value="${jdbc.driver.class}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--Spring整合 Hibernate JPA ,配置EntityManagerFactory--> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!--hibernate相关属性的注入:--> <!--database配置数据库类型--> <property name="database" value="MYSQL"/> <!--开启正向工程,自动创建表--> <property name="generateDdl" value="true"/> <!--开启显示执行的sql--> <property name="showSql" value="true"/> </bean> </property> <!--配置要扫描的实体的包--> <property name="packagesToScan"> <list> <value>com.haiexijun.pojo</value> </list> </property> </bean> <!--配置Hibernate的事务管理器--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!--开启注解事务处理--> <tx:annotation-driven transaction-manager="transactionManager"/> <!--配置springIOC的注解扫描--> <context:component-scan base-package="com.haiexijun"/> <!--Spring Data JPA的配置--> <!--base-package:扫描dao接口所在的包--> <jpa:repositories base-package="com.haiexijun.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/> </beans>
jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/hibernate02 jdbc.driver.class=com.mysql.cj.jdbc.Driver jdbc.username=root jdbc.password=zc20020106
2.创建dao继承JpaRepository就好了,不用去写任何CRUD的接口的实现。
package com.haiexijun.dao; import com.haiexijun.pojo.Users; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface usersDao extends JpaRepository<Users,Integer> { }
下面是一些测试代码:
import com.haiexijun.dao.usersDao; import com.haiexijun.pojo.Users; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; 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 javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class testDao { @Autowired private usersDao usersDao; @PersistenceContext(name = "entityManagerFactory") private EntityManager entityManager; // 测试spring data jpa的环境 @Test @Transactional @Rollback(value = false) public void testInsertUser(){ Users users=new Users(); users.setUserName("李四"); users.setUserAge(23); usersDao.save(users); } //测试usersDao注入的到底是什么 @Test public void test01(){ System.out.println(usersDao); //org.springframework.data.jpa.repository.support.SimpleJpaRepository@5f3b84bd System.out.println(usersDao.getClass()); //class com.sun.proxy.$Proxy59 //会发现注入的是一个代理对象 System.out.println("======"); JpaRepositoryFactory factory=new JpaRepositoryFactory(entityManager); //factory.getRepository()方法可以帮助我们为接口生成实现类,而这个实现类是SimpleJpaRepository的代理对象 //接口必须要继承Repository接口 usersDao ud = factory.getRepository(com.haiexijun.dao.usersDao.class); System.out.println(ud); System.out.println(ud.getClass()); } }
3.Repository接口详解
Repository接口是Spring Data JPA中为我们提供的所有接口中的顶层接口
repository提供了两种查询方式的支持:
(1)基于方法名称的命名规则查询
规则是什么?
findBy+属性名称(属性名称的首写字母要大写)+查询条件(首字母要大写)
具体更多的规则查看Spring官网即可。
userDao02:
package com.haiexijun.dao; import com.haiexijun.pojo.Users; import org.springframework.data.repository.Repository; import java.util.List; @org.springframework.stereotype.Repository public interface userDao02 extends Repository<Users,Integer> { List<Users> findByUserNameIs(String name); List<Users> findByUserNameLike(String name); List<Users> findByUserNameAndUserAgeGreaterThanEqual(String name, Integer age); }
测试用例:
// 根据姓名查询数据 @Test @Transactional @Rollback(value = false) public void test02(){ //判断相等的条件有三种方式 //1.什么都不写 2.is 3.equal List<Users> list= userDao02.findByUserNameIs("张三"); System.out.println(list); } //根据用户姓名Like处理 @Test @Transactional @Rollback(value = false) public void test03(){ List<Users> list= this.userDao02.findByUserNameLike("张%"); System.out.println(list); } //查询名称为王五,并且他的年龄大于等于20岁 @Test @Transactional @Rollback(value = false) public void test04(){ List<Users> list=userDao02.findByUserNameAndUserAgeGreaterThanEqual("李四",20); System.out.println(list); }
(2)基于@Query注解查询
也有如下两种方式:
通过JPQL语句查询:
JPQL是通过Hibernate的HQL演变过来的。他和HQL语法极其相似。
//使用@Query注解 // 可以用?index,index从1开始来传参。如:from Users where userName=?1 //也可以用下面这种@param来传参数 @Query(value = "from Users where userName=:name") List<Users> queryUsersByUserNameUseJPQL(@Param("name") String name); @Query(value = "from Users where userName like :name") List<Users> queryUsersByNameLikeUseJPQL(@Param("name") String name); @Query("from Users where userName=?1 and userAge>?2") List<Users> queryByUserNameAndUserAgeGreaterThanEqualUseJPQL(String name, int age);
@Query注解还可以通过sql语句来查询:
// 使用@Query注解查询SQL语句 //要开启nativeQuery为true @Query(value = "select * from t_users where username=? ",nativeQuery = true) List<Users> queryUsersByNameUseSQL(String name); @Query(value = "select * from t_users where username like ?",nativeQuery = true) List<Users> queryUsersByNameLikeUseSQL(String name); @Query(nativeQuery = true,value = "select * from t_users where username=? and userage>?") List<Users> queryByUserNameAndUserAgeGreaterThanEqualUseSQL(String name, int age);
(3)下面演示@Query注解的更新操作:
jpql编写 DAO接口update语句 还要添加@Modifying注解:
//通过@Query注解进行数据更新 //jpql编写 DAO接口update语句 要添加@Modifying注解 @Query("update Users set userAge =?2 where userId =?1 ") @Modifying void updateUserAgeById(Integer id,Integer age);
4.PagingAndSortingRepository接口
这个接口主要是帮助我们完成分页和排序处理。
(1)分页处理
我们创建一个新的Dao接口,继承至PagingAndSortingRepository接口。
我们点开这个接口的源代码查看后发现,这两个接口有2个重载的方法。这两个方法需要传入不同的参数,一个需要Sort类型的参数(用于排序),一个需要Pageable类型的参数(用于分页)。并且我们也会发现这两个方法的名称都叫做findAll(),也就是说,是对数据库表当中的所有的数据进行查询的。
package com.haiexijun.dao; import com.haiexijun.pojo.Users; import org.springframework.data.repository.PagingAndSortingRepository; //继承自PagingAndSortingRepository这个接口,并且这个接口里面要传入一个泛型,泛型的一个个参数是要操作的表对应的实体类,第二个参数为表主键的数据类型 public interface UserDao01 extends PagingAndSortingRepository<Users,Integer> { }
如果你比较懒,不想再创建一个人接口,也没有关系,因为我们之前用的UsersDao就是就继承自JPArepository接口,而JPArepository接口又继承了PagingAndSortingRepository这个接口,也就是说你可以用之前得Dao就可以了。
然后回到我们的测试代码当中。编写如下的测试方法来学习这个接口的相关操作。
比如说我要对数据表中所有的数据做分页处理:
/** * 分页 */ @Test @Transactional @Rollback(value = false) public void test12(){ //这里注意,Pageable是接口,我们不能直接new这个接口,而应该new他的接口实现类PageRequest。 Integer page=0;// page:当前页得索引,丛林开始 Integer size=3;// size:每页显示几条数据,这里是三条 Pageable pageable =PageRequest.of(page,size); Page<Users> p= usersDao.findAll(pageable); System.out.println("数据得总条数:"+p.getTotalElements()); System.out.println("数据的总页数:"+p.getTotalPages()); List<Users> list=p.getContent(); System.out.println("分页得结果:"+list); }
(2)排序处理
/** * 排序 * 对单列做排序处理 */ @Test @Transactional @Rollback(value = false) public void test13(){ // Sort对象封装了排序规则以及指定的排序字段(用对象的属性名来表示) //Sort构造方法可以传入两个参数 //第一个参数direction:排序规则 //第二个参数properties:指定做排序的属性 Sort sort=Sort.by(Sort.Direction.DESC,"userId"); List<Users> list=usersDao.findAll(sort); System.out.println(list); } /** * 排序 * 对多列做排序处理 */ @Test @Transactional @Rollback(value = false) public void test14(){ // Sort对象封装了排序规则以及指定的排序字段(用对象的属性名来表示) //Sort构造方法可以传入两个参数 //第一个参数direction:排序规则 //第二个参数properties:指定做排序的属性 //Order的构造参数与Sort的一样 Sort.Order order1=new Sort.Order(Sort.Direction.DESC,"userAge"); Sort.Order order2=new Sort.Order(Sort.Direction.ASC,"userId"); Sort sort=Sort.by(order1,order2); List<Users> list=usersDao.findAll(sort); System.out.println(list); }