SpringData JPA 详解(自定义查询、分页、事务控制)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ditto_zhou/article/details/82769073

简介

SpringData JPA是 JPA的一种实现,极大的简化了JPA的开发,原始JPA的开发,需要创建实体管理工厂,使用实体管理器定义各种查询进行CRUD操作,而SpringData JPA只需要通过核心接口Repository和它的子类就能很方便的操作数据库。

Repository

1. Repository:最顶层的接口,一个空的接口,统一所有的Repository类型,并且能够让组件扫描的时候能够自动识别

2. CrudRepository: Repository的子接口,提供CRUD的操作

3. PagingAndSortingRepository: CrudRepository的子接口,添加了分页和排序的功能

4. JpaRepository: 是PagingAndSortingRepository的子接口,增加一些实用的功能,比如批量操作

5. JpaSpecificationExecutor:来定义复杂查询

使用SpringData JPA

将SpringDataJPA整合Spring,将EntityManagerFactory的创建交给Spring容器,只需要在xml中配置JPA的Repository的接口位置,就可以很方便的获取到Repository的bean,使用时候直接在service层注入定义的repository.

配置文件:

<?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.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="cn.bing"/>

    <!-- 配置数据库资源文件的位置-->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- jdbc.properties 中的key必须定义为 jdbc.username,格式开头的 -->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
    </bean>

    <!-- 2. 配置 JPA 的 EntityManagerFactory -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置jpa的适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <!-- 配置实体所在的包 -->
        <property name="packagesToScan" value="cn.bing.pojo"/>
        <!-- 配置jpa的属性 -->
        <property name="jpaProperties">
            <props>
                <!-- 二级缓存相关 -->
                <!--
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop>
                -->
                <!-- 生成的数据表的列的映射策略 -->
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <!-- hibernate 基本属性 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

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

    <!-- 4. 配置支持注解的事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的命名空间 -->
    <!-- base-package: 扫描 Repository Bean 所在的 package -->
    <jpa:repositories base-package="cn.bing.dao"
                      entity-manager-factory-ref="entityManagerFactory"/>

</beans>

1 . 引入Jar

2. 创建实体

@Entity表示这个类是一个实体类,参与JPA和数据库表的映射

@Table表示具体映射的表名

@Id表示这个字段是主键,@GeneratedValue表示这个主键的生成策略

@Column表示映射到数据库的表的字段名,字段名和表字段不一致,修改注解@Column的name属性

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column
    private Integer age;
    @Column
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3. 编写接口继承Repository的子类,并且需要在spring的配置文件配置

 <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的命名空间 -->
    <!-- base-package: 扫描 Repository Bean 所在的 package -->
    <jpa:repositories base-package="cn.bing.dao" 
                      entity-manager-factory-ref="entityManagerFactory"/>

一般都是选择继承JpaRepository方便直接调用现有的方法进行CRUD,继承JpaSpecificationExecutor则是方便定义一些复杂的查询。

JpaRespository<T,D> T 表示实体,D表示实体的主键ID类型

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
   
}

 自定义接口继承父类的Repository后,自动获取的操作数据的API,只能进行简单的操作,需要自定义查询。

repository.findOne(1);//根据id查询
repository.findAll();//查询
repository.delete(new User());//delete操作
repository.saveAndFlush(new User());//insert操作
   

关于SpringData JPA查询的定义

1. spring data 对于定义方法的查询策略

查询策略是spring data 根据方法名称取解析用户的查询意图,第一种,根据方法的命名规则解析,第二种是通过Query去解析,如果两种同时存在时,springdata按照那种解析方法名,这就是spring data的查询策略,查询策略可以在<jpa:repositorys/> 属性query-lookup-strategy 配置

CREATE: 通过解析方法的名称来创建查询,也就是下面的规则1

USE_DECLARED_QUERY:根据定义好的语句去查询,如果找不到,抛出异常信息。查询语句定义在某个注解或者方法上。

CREATE_IF_NOT_FOUND:优先查询方法上是否有定义好的查询语句,如果没有,则按照方法名称解析,这是默认的策略。

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    //根据springData的关键字命名方法名称,可以不用写实现类
    List<User> findByNameAndAge(String name, Integer age);

    List<User> findByNameLikeAndAge(String name, Integer age);
    //@Query注解里面写JPQL语句,定义查询
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);
    //@Modifying修饰方法,告知SpringData,这是一个UPATE或者DELETE操作
    //在service层的方法上添加事务操作
    @Modifying
    @Query(nativeQuery = true,value = "update user set name = ?1  where id = ?2 ")
    int updateUserNameById(String name,Integer id);

}

规则1:根据SpringData JPA的关键字定义方法名,方法的参数顺序和关键字的顺序一致,名称可以不一致。

Spring Data JPA对方法名称进行解析的时候,会将一些无用的前缀名自动去除,比如find、findBy、read、readBy,然后根据关键字解析成对应JPQL语句。也要注意方法的参数顺序和定义的关键字的顺序一致。

 规则2:定义方法时候,上面加上注解@Query,默认nativeQuery是false,此时value填入的是JPQL语句,修改nativeQuery是true,就能够写入SQL语句

//@Query注解里面写JPQL语句,定义查询
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);

注意参数注入的方式有两种:

1. ?下标,下标从1开始

2.  :xxx ...:yyy,xxx和yyy必须是实体的属性名,并且方法参数上加上对应的注解@Param("xxx")和@Param('yyy')

@Modify和事务

可以通过JPQL语句定义update/delete,此时在@Query注解中定义,必须加上@Modify,告诉spring data 这是一个update/delete操作。

update/delete操作需要事务支持,必须在service层,添加事务,因为spring data,默认情况下每个方法是只读事务,不能完成update/delete操作。

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    //根据springData的关键字命名方法名称,可以不用写实现类
    List<User> findByNameAndAge(String name, Integer age);
    List<User> findByNameLikeAndAge(String name, Integer age);
    //@Query注解里面写JPQL语句,定义查询
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);
    //@Modifying修饰方法,告知SpringData,这是一个UPATE或者DELETE操作
    //在service层的方法上添加事务操作
    @Modifying
    @Query(nativeQuery = true,value = "update user set name = ?1  where id = ?2 ")
    int updateUserNameById(String name,Integer id);
}
@Service
public class UserServiceImpl implements  UserService {
    @Autowired
    private UserRepository repository;
    //对于SpringData jpa的update或者delete操作,必须在service层使用事务,直接使用仓库的方法会报错
    //另外,SpringData jpa 的 JPQL语法不支持insert
    @Transactional(propagation = Propagation.REQUIRED)
    public int updateUserNameById(String name,Integer id){
        return repository.updateUserNameById(name,id);
    }

}
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
    @Autowired
    private UserRepository repository;
    @Autowired
    private UserService service;

    @Test
    public void test(){
        service.updateUserNameById("张三",1);
    }
}
        

分页和模糊查询

 模糊查询

需要注意的是%%外面不需要再加上''

//模糊查询
    @Query(nativeQuery = true,value = " select  * from user where name like %?1% ")
    User findUserByLikeName(String name);

分页对象

1. 要求自定义的Repository必须继承了PagingAndSortingRepository或者他的子类JpaRepository

2. 分页对象是Pageable接口的实现类PageRequest

public class PageRequest implements Pageable, Serializable {
    private static final long serialVersionUID = 8280485938848398236L;
    private final int page;//页码,从0开始,0表示第一页,1表示第二页,以此类推
    private final int size;//每页显示的记录数
    private final Sort sort;//排序规则

 Sort对象,定义排序规则,常用的是下面这种构造函数,支持可变参数的

public Sort(Sort.Direction direction, String... properties) {
        this(direction, (List)(properties == null ? new ArrayList() : Arrays.asList(properties)));
    }

 定义分页查询,只需要将查询的参数和分页对象作为参数。

 Page<User> findByNameLike(String str , Pageable pageable);
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
    @Autowired
    private UserRepository repository;
    @Test
    public void test(){
       /**
        * SpringData jpa 的分页
        * Pageable接口的实现类是PageRequest,Page接口的实现类是PageImpl。
        */
       Pageable page = new PageRequest(0,2,new Sort(Sort.Direction.DESC,"id"));
       Page<User> personList =  repository.findByNameLike("张%",page);
        System.out.println("总记录数" + personList.getTotalElements());
        System.out.println("当前第几页: " + (personList.getNumber() + 1));
        System.out.println("总页数: " + personList.getTotalPages());
        System.out.println("当前页面的记录数:" + personList.getContent());
        System.out.println("当前页面的记录数: " + personList.getNumberOfElements());
    }
}

猜你喜欢

转载自blog.csdn.net/Ditto_zhou/article/details/82769073
今日推荐