Spring Data JPA入门简解

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

SpringData有许多子项目,其中一个为Spring Data JPA 。

【1】JPA

JPA是sun提出的一个对象持久化规范,各JavaEE应用服务器自主选择具体实现。

JPA的设计者是Hibernate框架的作者,因此Hibernate作为Jboss服务器中JPA的默认实现;Oracle的Weblogic使用EclipseLink(以前叫TopLink)作为默认的JPA实现;IBM的Websphere和Sun的Glassfish默认使用OpenJPA(Apache的一个开源项目)作为其默认的JPA实现。

JPA的底层实现是一些流行的开源ORM(对象关系映射)框架,因此JPA其实也就是java实体对象和关系型数据库建立起映射关系,通过面向对象编程的思想操作关系型数据库的规范。


JPA和Hibernate的关系

JPA 是 hibernate 的一个抽象(就像JDBC和JDBC驱动的关系)。

JPA 是规范:JPA 本质上就是一种 ORM 规范,不是ORM 框架 —— 因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由 ORM 厂商提供实现。

Hibernate 是实现:Hibernate 除了作为 ORM 框架之外,它也是一种 JPA 实现

从功能上来说, JPA 是 Hibernate 功能的一个子集。


spring data jpa 、hibernate以及JPA的关系

JPA是一种规范,而hibernate是实现这种规范的底层实现,spring data jpa对持久化接口JPA再抽象一层,针对持久层业务再进一步简化。

这样开发者就连持久层的业务逻辑也不用写了,只要按照spring data jpa的命名规范,写好接口继承,一切就交给spring吧。


JPA的优势

标准化: 提供相同的 API,这保证了基于JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。

简单易用,集成方便: JPA 的主要目标之一就是提供更加简单的编程模型,在 JPA 框架下创建实体和创建 Java 类一样简单,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单,

可媲美JDBC的查询能力: JPA的查询语言是面向对象的,JPA定义了独特的JPQL,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

支持面向对象的高级特性: JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,最大限度的使用面向对象的模型


JPA相关技术

JPA 包括 3方面的技术:ORM 映射元数据,JPA 的 API,查询语言(JPQL)。

ORM 映射元数据:JPA 支持 XML 和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。

JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有的事情,开发者从繁琐的 JDBC和 SQL代码中解脱出来。

查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序和具体的 SQL 紧密耦合。


【2】Spring Data JPA几个核心接口

① Repository

根接口,其他接口继承该接口。源码如下:

/**
 * Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
 * purpose is to hold type information as well as being able to discover interfaces that extend this one during
 * classpath scanning for easy Spring bean creation.
 * <p>
 * Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
 * same signature as those declared in {@link CrudRepository}.
 * 
 * @see CrudRepository
 * @param <T> the domain type the repository manages
 * @param <ID> the type of the id of the entity the repository manages
 * @author Oliver Gierke
 */
public interface Repository<T, ID extends Serializable> {

}

② CrudRepository

基本的增删改查接口,源码如下:

/** Interface for generic CRUD operations on a repository for a specific type.*/
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

    /* Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
     * entity instance completely. */
    <S extends T> S save(S entity);

    /* Saves all given entities.*/
    <S extends T> Iterable<S> save(Iterable<S> entities);

    /* Retrieves an entity by its id.*/
    T findOne(ID id);

    /** Returns whether an entity with the given id exists.*/
    boolean exists(ID id);

    /* Returns all instances of the type. */
    Iterable<T> findAll();

    /* Returns all instances of the type with the given IDs.*/
    Iterable<T> findAll(Iterable<ID> ids);

    /* Returns the number of entities available.*/
    long count();

    /** Deletes the entity with the given id.*/
    void delete(ID id);

    /** Deletes a given entity.*/
    void delete(T entity);

    /** Deletes the given entities.*/
    void delete(Iterable<? extends T> entities);

    /* Deletes all entities managed by the repository.*/
    void deleteAll();
}

③ PagingAndSortingRepository

增加了分页和排序操作,源码示例如下:

/**
 * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and
 * sorting abstraction.
 */
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

    /* Returns all entities sorted by the given options.*/
    Iterable<T> findAll(Sort sort);

    /*Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.*/
    Page<T> findAll(Pageable pageable);
}

④ JpaRepository

添加了批量操作,并从写了了父接口一些方法的返回类型,源码示例如下:

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable>
        extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#findAll()
     */
    List<T> findAll();

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
     */
    List<T> findAll(Sort sort);

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
     */
    List<T> findAll(Iterable<ID> ids);

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
     */
    <S extends T> List<S> save(Iterable<S> entities);

    /**
     * Flushes all pending changes to the database.
     */
    void flush();

    /* Saves an entity and flushes changes instantly.*/
    <S extends T> S saveAndFlush(S entity);

    /**
     * Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear
     * the {@link javax.persistence.EntityManager} after the call.
     * 
     * @param entities
     */
    void deleteInBatch(Iterable<T> entities);

    /**
     * Deletes all entities in a batch call.
     */
    void deleteAllInBatch();

    /**
     * Returns a reference to the entity with the given identifier.
     * 
     * @param id must not be {@literal null}.
     * @return a reference to the entity with the given identifier.
     * @see EntityManager#getReference(Class, Object)
     */
    T getOne(ID id);

    /* (non-Javadoc)
     * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
     */
    @Override
    <S extends T> List<S> findAll(Example<S> example);

    /* (non-Javadoc)
     * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
     */
    @Override
    <S extends T> List<S> findAll(Example<S> example, Sort sort);

}

⑤ JpaSpecificationExecutor

用来做动态查询,源码示例如下:

/*Interface to allow execution of {@link Specification}s based on the JPA criteria API.
*/
public interface JpaSpecificationExecutor<T> {

    /* Returns a single entity matching the given {@link Specification}.*/
    T findOne(Specification<T> spec);

    /* Returns all entities matching the given {@link Specification}.*/
    List<T> findAll(Specification<T> spec);

    /*Returns a {@link Page} of entities matching the given {@link Specification}.*/
    Page<T> findAll(Specification<T> spec, Pageable pageable);

    /*Returns all entities matching the given {@link Specification} and {@link Sort}. */
    List<T> findAll(Specification<T> spec, Sort sort);

    /**
     * Returns the number of instances that the given {@link Specification} will return */
    long count(Specification<T> spec);
}

⑥ Specification
Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可。

源码示例如下:

public interface Specification<T> {

    /**
     * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
     * {@link Root} and {@link CriteriaQuery}.
     * 
     * @param root
     * @param query
     * @return a {@link Predicate}, may be {@literal null}.
     */
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

【3】Spring Data JPA实例

① persistence.xml

根据JPA规范要求,配置persistence.xml,并存在于类路径下的 META-INF 目录中,这里我们配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

    <persistence-unit name="springJpa" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <!-- 
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.url" value="com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="1qaz2wsx"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
            -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="false"/>
            <property name="hibernate.hbm2ddl.auto" value="none"/>

            <!-- 配置hibernate缓存 -->
            <property name="hibernate.cache.use_second_level_cache" value="true"/>
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
            <property name="hibernate.generate_statistics" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

可以把数据库的一些配置放到application.properties的配置文件中:

hibernate.dialect=org.hibernate.dialect.MySQLDialect
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc.username=root
jdbc.password=root

② 配置spring的applicationContext.xml文件,配置数据源,事务管理器,以及spring data jpa 的扫描目录

<!-- 数据源配置 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="filters" value="stat" />
        <property name="maxActive" value="20" />
        <property name="initialSize" value="1" />
        <property name="maxWait" value="60000" />
        <property name="minIdle" value="1" />
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="poolPreparedStatements" value="false" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="20" />
    </bean>

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

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="springJpa" />
    </bean>

    <!-- 启用 annotation事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 配置Spring Data JPA扫描目录 -->
    <jpa:repositories base-package="com.xxx.**.dao"
        repository-impl-postfix="Impl" entity-manager-factory-ref="entityManagerFactory"
        transaction-manager-ref="transactionManager" />

③ 编写我们的Repository

在Spring Data JPA扫描目录下,也就是持久层下新建一个接口继承于JpaRepository:

public interface TaskDao extends JpaRepository<Task,Long>{
}

在父接口中,Spring Data JPA已经帮我定义好一些crud和分页操作接口,我们直接用就可以了。


④ service
在service层新建TaskService,并把TaskDao 注入到该bean中:

@Service
public class TaskService {

  @Autowired TaskDao taskDao ;
  /**
     * 保存更新实体类
     * @param task
     * @return
     */
     @Transactional
    public Task saveOrUpdateTask(Task task){
        if(task.getId() > 0){ //更新操作
            task.setTaskName("newTaskName");
        }else{ //保存操作
            task.setCreateTime(new Date());
        }
        return this.taskDao.save(task); //save会根据实体类是否有id进行保存或者更新操作
    }

    /**
     * 根据id删除一个实体
     * @param id
     */
     @Transactional
    public void deleteTask(Long id){
        this.taskDao.delete(id);

    }

    /**
     * 获取分页数据
     * @param page
     * @param size
     * @return
     */
    public Page<Task> findPageTask(int page ,int size){

        //直接分页不排序
        Page<Task> list1 = this.taskDao.findAll(new PageRequest(page, size));

        //如果需要分页和排序
        Sort sort = new Sort(new Order("createTime"));
        Page<Task> list2 = this.taskDao.findAll(new PageRequest(page, size,sort));

        return list1 ;
    }

    /**
     * 根据id获取一个实体
     * @param id
     * @return
     */
    public Task findOne(Long id){
        return this.taskDao.findOne(id);
    }
}

⑤ Dao中添加自定义方法

示例如下:

public interface TaskDao extends JpaRepository<Task,Long>{
   /**
     * 根据任务名获取任务列表
     * @param taskName
     * @return
     */
    List<Task> findByTaskName(String taskName);
}

只需在service调用这个方法就可以,当然你还可以利用@Query直接在方法名上自定义查询。

public interface TaskDao extends JpaRepository<Task,Long>{
   /**
     * 根据任务名获取任务列表
     * @param taskName
     * @return
     */
    @Query("select * from Task t where t.taskName = ?")
    List<Task> findByTaskName(String taskName);
}

还可以写原生的sql语句:

public interface TaskDao extends JpaRepository<Task,Long>{
   /**
     * 根据任务名获取任务列表
     * @param taskName
     * @return
     */
    @Query(value = "select * from tb_task t where t.task_name = ?",nativeQuery = true)
    List<Task> findByTaskName(String taskName);
}

猜你喜欢

转载自blog.csdn.net/j080624/article/details/80833259