Spring Data JPA

前言

spring Data项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,使数据库访问变得方便快捷。包括非关系数据库Map-Reduce框架、云数据服务等等,另外也包含对关系数据库的访问支持。

JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式
SpringDataJPA是一个JPA数据访问抽象,需要第三方实现,有各种实现JPA的提供者。
  • Spring Data JPA是Spring提供的持久层的解决方案
  • Spring Data JPA其实就是Spring对JPA操作的封装(entiyManager)
  • 使用Spring Data JPA有两个条件:
    • Spring整合JPA
    • 需要有一个JPA的实现框架

运行过程与原理剖析

Spring Data JPA 封装了 JPA 规范操作逻辑,Hibernate实现了JPA规范,并且封装了jdbc操作逻辑,可以进行数据库CRUD。我们使用Spring Data JPA接口就能更加方便的操作数据库。

1. 我们需要做的是创建一个接口:UserDao(extends JpaRepository<User, Long>, JpaSpecificationExecutor<User>)
2. Spring 通过 JdkDynamicAopProxy 的 invoke 方法创建了一个动态代理:SimpleJpaRepository
3. SimpleJpaRepository封装了JPA的操作(借助JPA的api完成数据库的crud)
4. 通过Hibernate完成数据库操作(封装了jdbc操作)

配置文件(application.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: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">

    <!--加载配置文件与要扫描的包-->
    <context:property-placeholder location="classpath:properties"></context:property-placeholder>
    <context:component-scan base-package="jpa"></context:component-scan>


    <!--数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>


    <!-- 创建entityManagerFactory对象交给spring容器管理 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--数据源-->
        <property name="dataSource" ref="dataSource"></property>

        <!--配置实体类扫描的包-->
        <property name="packagesToScan" value="jpa.entity"></property>

        <!--配置jpa实现-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>

        <!--jpa的供应商适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--配置是否自动创建数据库表-->
                <property name="generateDdl" value="true"></property>

                <!--数据库方言:支持的特有语法 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>

                <property name="database" value="MYSQL"></property>
                <property name="showSql" value="true"></property>
            </bean>
        </property>

        <!--jpa方言-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>

        <!--注入jpa的配置信息
            加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息
            hibernate.hbm2ddl.auto : 自动创建数据库表
                            create : 每次都会重新创建数据库表
                            update : 有表不会重新创建,没有表会重新创建表
        -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

    </bean>


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


    <!--声明式事务-->
    <tx:annotation-driven transaction-manager="tm"></tx:annotation-driven>


    <!--整合spring data jpa-->
    <jpa:repositories base-package="jpa.dao"
                      transaction-manager-ref="tm"
                      entity-manager-factory-ref="entityManagerFactory">
    </jpa:repositories>

</beans>

基本查询

UserDao extends JpaRepository<User, Long>

    /**
     * 基本CRUD操作
     * save(user) : 保存或者更新
     *        根据传递的对象是否存在主键id,
     *        如果没有id主键属性:保存
     *        存在id主键属性,根据id查询数据,更新数据
     *
     * delete(id)
     *        根据id删除:调用delete(id)方法
     *
     * findOne(id)
     *        根据id查询:调用findOne(id)方法
     *
     * getOne(id)
     *       根据id查询:调用getOne(id)方法  ,延迟加载(需要@Transactional)
     *
     * count()
     *       查询全部客户数量
     */

复杂查询

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


    /**
     * 使用JPQL的方式查询
     */
    @Query(value = "from User  where userId = ? ")
    public User findById(long id);

    /**
     * 通过使用 @Query 来执行一个更新操作,
     * 为此,我们需要在使用 @Query 的同时,
     * 用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询
     */
    @Query(value = "update User  set userName = ?2 where userId = ?1 ")
    @Modifying
    public void update(long id,String name);

    /**
     * sql语句查询
     */
    @Query(value = "select * from user",nativeQuery = true)
    public List<User> findAll();

    /**
     * 方法名查询:findBy + 对象中的属性名称(首字母大写)
     */
    User findByUserName(String userName);

    /**
     * 方法名模糊查询查询:findBy + 对象中的属性名称 + Like(传递参数:"ld%")
     */
    List<User> findByUserNameLike(String userName);

    /**
     * 多条件连接(And|Or)查询:findBy + 对象中的属性名称 + Like + And + 对象中的属性名称
     */
    List<User> findByUserNameLikeAndUserAge(String a,Integer b );
}

动态查询

Specification:动态查询

JpaSpecificationExecutor 接口的方法:
/**
 * 根据条件查询一个对象
 *  	T findOne(Specification<T> spec);
 * 根据条件查询集合
 *  	List<T> findAll(Specification<T> spec);
 * 根据条件分页查询
 *  	Page<T> findAll(Specification<T> spec, Pageable pageable);
 * 排序查询查询
 *  	List<T> findAll(Specification<T> spec, Sort sort);
 * 统计查询
 *  	long count(Specification<T> spec);
 *
 * Root接口  ,代表查询的根对象,可以通过root获取实体类中的属性
 * query	:代表一个顶层查询对象,用来自定义查询
 * cb		:用来构建查询,此对象里有很多条件方法
 *
 * public Predicate toPredicate(Root<T> root,CriteriaQuery<?> query,CriteriaBuilder cb);
 */

public class SpecificationJpaTest {

    @Autowired
    private UserDao dao;

    public  void findOne(){
        Specification<User> cu = new Specification<>() {
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                /**
                 * 获取比较的属性:
                 *      1.Path<Object> userName = root.get("userName");
                 *      2.构造查询条件  :
                 *              select * from user where user_name = '传智播客';
                 *
                 *      第一个参数:需要比较的属性(path对象)
                 *      第二个参数:当前需要比较的取值
                 *
                 */
                return cb.equal(root.<String>get("userName"),"传智播客");
            }
        };
        User one = dao.findOne(cu);
    }

    public  void findByPage(){
        PageRequest pageRequest = new PageRequest(0, 2);
        Specification<User> cu = new Specification<User>() {
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                /**
                 * 构造查询
                 *
                 * 1.构造名称的精准匹配查询(第一个参数,path(属性),第二个参数,属性的取值)
                 *     Predicate p1 = cb.equal(userName, "传智播客");
                 *
                 * 2.构造年龄的精准匹配查询
                 *     Predicate p2 = cb.equal(userAge, 12);
                 *
                 * 3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系cb.and(),满足条件一或满足条件二即可:或关系cb.or())
                 *     Predicate predicate = cb.and(p1, p2);
                 *     return predicate;
                 */
                return cb.like(root.<String>get("userName"),"传智%");
            }
        };
        Page<User> one =dao.findAll(cu,pageRequest);
    }

    /**
     * 添加排序:
     *         创建排序对象,需要调用构造方法实例化sort对象
     *         第一个参数:排序的顺序(倒序,正序)
     *            Sort.Direction.DESC        :倒序
     *            Sort.Direction.ASC         :升序
     *
     *         第二个参数:排序的属性名称
     *              Sort sort = new Sort(Sort.Direction.DESC,"userId");
     *              List<User> users = dao.findAll(spec, sort);
     */

}

对象导航查询(Spring Data JPA )

查询一个对象的同时,通过此对象查询它的关联对象(默认懒加载)(需要在实体对象中配置外键(多表关系)@OneToMany)

Boss boss = bossDao.getOne(1l);
Set<Customer> customers = boss.getCustomers();
原文地址:https://www.cnblogs.com/loveer/p/11379814.html