一、JPA 介绍

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 一句话,JPA 只是一种接口、一种规范、比如Hibernate、OpenJPA 这些就属于Jpa的具体实现。

二、Spring Data JPA 解决的问题

Spring 为什么要弄出来这个Spring Data JPA 呢? 可以看看以下的代码,然后思考这些代码有什么弊端?
  • Dao层接口的定义
public interface UserDao{
  
  void save(User user);
  
  void delete(User user);
  
  ...
}
  • Dao具体的实现类

public class UserDaoImpl extends HibernateDaoSupport implements UserDao{
  
  public void save(User user){
    getHibernateTemplate().save(user);
  }
  
  public void delete(User user){
    getHibernateTemplate().delete(user);
  }
  
  ...
}
咋一看上,可能会说,没啥问题呀~多简单明了,别说我这种小学三年级的能看得懂,就算是小学四年级也能看得懂。 诚如这些想法一样,这样的代码已经很精简了,就一句话而已, 难道我们的dao的实现类能再精简么,变成什么都不写么? 这个想法确实还真被spring给想到了。
  • 问题所在
上述代码有两个问题:
1. 仔细观察两个具体实现方法, 不管以后是保存用户还是保存商品、或者是保存订单,都是一个模子刻出来的,唯一的不同只是具体操作的数据不同而已,当然删除也是一样。
2. 我们现在使用的是Hibernate 这一种ORM技术,如果以后我们想换成别的ORM 技术,那么需要修改代码。这就违反了`开-闭原则` 【对扩展开发、对修改关闭】 ,要不就是重新打造新的实现类,抛弃旧的实现类。

那么有没有一种方案,让我们能够以不变应万变 ,轻而易举解决掉上面的两个问题? Spring Data JPA是一个完美的解决方案。
在JavaEE 5.0发布的时候,sun公司就提出了jpa的规范,希望整合ORM技术,实现天下归一 。 虽然我们学过的orm技术只有hibernate、但是其实orm技术还有其他的一些方案,比如Apache的 openJPA。 Spring 作为业务逻辑层框架,起到承上启下的作用。所以它对JPA的这些技术实现做了一套封装。只要按照规范来配置即可,甚至你们的dao层实现都不用实现了,它在内部给你实现,如果我们想换到其他的jpa实现方案,那么只需要修改配置即可。

三、Spring Data JPA 下载

  • 官网地址

https://projects.spring.io/sp...

  • 包含的模块

Spring-data-commonsSpring-data-jpa

下面摘录官网对这两个模块的介绍

这个模块是所有Spring Data 系列工程的核心模块。

这个模块是专注于JPA这种操作的模块

Spring Data 还针对其他的不同类型技术提供支持比如: Spring Data RedisSpring Data JDBCSpring Data MongoDB

四、Spring Data JPA 入门

该小节,我们需要使用Spring 来配置 data jpa ,并且完成一个简单的查询所有操作。

1. 导入jar包

spring的jar包
    core | context | beans | expression 4个核心
    aop相关jar包 4个
    spring-orm-xxx.jar 、spring-jdbc-xxx.jar 、 spring-tx-xxx.jar 、spring-test-xxx.jar
    日志输出jar  4个


hibernate的jar包
    核心必须包
    
其实上面这些也不用看了,就和我们以前整合Spring + Hibernate一样。但是如果要想使用Spring Data JPA 我们需要额外导入以下的jar包

spring-data-commons.jar 、 spring-data-jpa.jar  、 hibernate-entitymanager-5.0.7.Final.jar

2. Dao层代码

这个CrudRepository 是spring已经针对JPA 定义好的一套增删改查的接口 , 我们继承这个接口即可。 实现类,我们也不用自己做了,后面可以让spring data jpa 帮忙创建这个接口的实现类
//继承接口,需要给定两个泛型,第一个User泛型是表示我们要查询的是哪一个表,这里写它对应的持久化类
//第二个泛型,写的是持久化类的主键类型 OID类型。
public interface UserDao extends CrudRepository<User, Integer> {

}

3. Service层代码

public interface UserService {

    List<User> findAll();
}

-------------------------------------------------

@Service
@Transactional // 其实这是查询的操作,开不开事务都可以运行。
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    
    @Override
    public List<User> findAll() {
        return (List<User>) userDao.findAll();
    }

}

4. 测试类代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testFindAll(){
        System.out.println(userService.findAll());
    }
}

5. 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:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:jpa="http://www.springframework.org/schema/data/jpa"
        xsi:schemaLocation="
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 导入jdbc.properties文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 使用c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    
    
    <context:component-scan base-package="com.unknow"/>
    
    <!-- 这里开始配置spring data jpa -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        
        <!-- 1. 设置如何连接数据库 -->
        <property name="dataSource" ref="dataSource"/>
        
        <!-- 2.可选的连接属性 -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
        
        <!-- 3. 设置采用什么技术  指定适配器,其实如果我们想换成别的jpa实现技术,只需要替换这个位置即可-->
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
        
        <!-- 4. 指定映射文件 -->
        <property name="packagesToScan" value="com.unknow.bean"></property>
    </bean>
    
    <!-- 5. 指定我们的dao层在哪个位置,以便data jpa 创建这些接口的动态代理。 -->
    <jpa:repositories base-package="com.unknow.dao"/>

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>    
    
    <!-- 6. 开启事务 -->
     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <!--如果只是查询的操作,这句话可以不用,但是上面的事务管理员必须声明,否则会报错。因为spring data里面会来检测是否有
    声明事务管理员,也不知道它用还是没用,总之它还是找了。 -->
     <tx:annotation-driven transaction-manager="transactionManager"/> 
</beans>        

五、Spring Data JPA 接口介绍

经过上面的入门例子,也看到了,spring data对于我们编程确实方便了许多。我们无需关心dao层的实现,只需要简单的照着规则去写代码即可,spring data jpa 最重要的就是我们的dao层继承的接口。接下来着重说明它的几个接口。

1. Repository

这个接口是所有接口的父接口,也就是它就是老大了。所有我们后面用的接口都是从这位兄弟身上扩展来的。 下图是Repository 这个接口的继承体系,图中的UserDao 和 MyRepository 是我自己写的,不用理会。 不过要声明的是: 这个Repository 是一个空接口!!!,里面没有任何方法。

在这里插入图片描述

package org.springframework.data.repository;

import java.io.Serializable;
/**
 * 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> {

}
这个repository虽然是个空接口,我们自己来继承它,虽然没有任何要实现的方法,但是我们可以自己写,自己声明方法,这也是允许D ,话不多说,走起~
  • 自定义接口 MyRepository
public interface MyRepository extends Repository<User, Integer> {

    /**
     * 根据用户id查询用户
     * @param uid
     * @return
     */
    User findByUid(int uid);

}
  • service层代码
public interface UserService {

    User findByUid(int uid);
}

------------------------------
@Service
@Transactional  //现在仍然是查询这个注解可以不写,为了免得记太多,我就索性都打开了。
public class UserServiceImpl implements UserService {    
    @Autowired
    private MyRepository repository;

    @Override
    public User findByUid(int uid) {
        return repository.findByUid(uid);
    }

}  
  
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
    
    @Autowired
    private UserService userService;
  
    @Test
    public void testFindByUid(){
        System.out.println(userService.findByUid(1));
        
    }
}
  • 结果:

在这里插入图片描述

  • 疑问:
写到这,有的绝对会有疑问, 这都可以,那这也行的话,我写一个叫做 fUid() 或者叫做 zhaoUserByUid() 这样的方法名,行不行呢? 答案是: NO!

因为我们声明的是接口,具体的实现类还是由人家的spring做出来的。spring胃口比较挑剔,而且为了满足大众化的口味,就做出了一些命名上的要求,要慢慢习惯这种要求,这也正是它的另一个框架Spring Boot的口号习惯优于配置 。 下图摘自 Spring Data JPA 文档对方法关键字的描述。

如:我们想按照用户的name 查询, 那么可以写成 findByName 以此类推

在这里插入图片描述
在这里插入图片描述

如果觉得这些关键字记不住,那么spring可以允许我们程序员自己定义自己的方法名。

2. 自定义方法名

这个小节主要讲述的是,我们可以自己定义自己的方法名,你爱叫啥叫啥,随意。
  • Dao接口
public interface MyRepository extends Repository<User, Integer> {


    @Query("from User  where uid = ?1") //?1 表示取第一个参数uid 来替代这个?。
    User selectUser(int uid);

}
  • Service
public interface UserService {

    User selectUser(int uid);
}
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
    
    @Autowired
    private UserService userService;

    @Test
    public void testSelectUser(){
        System.out.println(userService.selectUser(1));
        
    }
}
虽然我们可以自己定义方法名,但是这种做法稍微有点麻烦,所以建议少用这种写法。 只有在以后执行多表查询的时候,我们才需要这么做。

3. CrudRepository

这个接口是扩展了repository, 在这个接口里面默认给大家声明好了一些增删改查的方法,此处给大家演练一个查询所有用户的操作
  • Dao
public interface UserDao extends CrudRepository<User, Integer> {
    //查询所有的用户
}
  • Service
@Service
//@Transactional
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserDao userDao;

    @Override
    public List<User> findAll() {
        return (List<User>) userDao.findAll();
    }

}
  • 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testFindAll(){
        List<User> list= userService.findAll();
        System.out.println("list="+list);
    }
}

4.PagingAndSortingRepository

这个接口是是CrudRepository的扩展接口,除了兼备增删改查之外,它还扩展了分页查询&排序的效果。

以下是这个接口的代码声明

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

    /**
     * Returns all entities sorted by the given options.
     * 
     * @param sort
     * @return 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.
     * 
     * @param pageable
     * @return a page of entities
     */
    Page<T> findAll(Pageable pageable);
}
  • Dao
public interface UserDao extends PagingAndSortingRepository<User, Integer> {
    //查询所有的用户
}
  • Service
public interface UserService {
    
    
    /**
     * 返回值是page 这个类型是springdatajpa定义好的。其实就和
     * 我们自己平常写的PageBean 一样。
     * @param pageable
     * @return
     */
    Page<User> findByPage(Pageable pageable);
}

---------------------------------------------------------
  
  @Service
//@Transactional
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserDao userDao;

    @Override
    public Page<User> findByPage(Pageable pageable) {
        return userDao.findAll(pageable);
    }

}
  • 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testFindByPage(){
        //这里ctrl + T 看实现类,然后找PageRequest 
        // page : page表示请求的页码数, 从0开始,0就代表第一页。
        //size : 表示每页拿多少条记录
        Pageable pageable = new PageRequest(1, 2);
        Page<User> page = userService.findByPage(pageable);
        
      //当前是第几页 这个页码从0开始的,大家可以加上1表示从1开始。
        System.out.println(page.getNumber()); 
        System.out.println(page.getTotalPages()); //总页数
        System.out.println(page.getSize()); //每页个数
        System.out.println(page.getTotalElements()); //总记录数
        System.out.println(page.getContent()); //总页数
    }
}

六、

  • JpaReposityry&多表查询

tolman
35 声望0 粉丝