1,什么是Spring Data JPA

Hibernate是数据访问解决技术的绝对霸主,使用O/R映射技术实现数据访问。

O/R映射即将领域模型类和数据表进行映射,通过程序操作对象而实现表数据操作的能力,让数据访问操作无需关注数据库相关的技术。

Spring Data JPA是Spring Data的一个子项目,他通过提供基于JPA的Repository极大地减少了JPA作为数据访问方案的代码量。

2,定义数据访问层

使用Spring Data JPA建立数据访问十分简单,只需定义一个继承JpaRepository的接口即可实现。定义如下:

public interface PersonRepository extends JpaRepository<Person,Log>{
	//定义数据访问操作的方法
}

3,配置使用Spring Data JPA

在Spring 环境中,使用Spring Data JPA可通过@EnableJpaRepository注解来开启Spring Data JPA的支持, @EnableJpaRepository接收的value参数用来扫描数据访问层所在包下的数据访问的接口定义。

4,定义查询方法

(1)根据属性名查询

  • 常规查询
public interface PersonRepository extends JpaRepository<Person,Long>{
/*通过名字相等查询,参数为name。相当于JPQL:select p from Person p where p.name=?1*/
List<Person> findByName(String name);
/*通过名字like查询,参数为name。相当于JPQL:select p from p where p.name like ?1*/
List<Person> findByNameLike(String name);
/*通过名字和地址查询,参数为name和address。相当于JPQL:select p from Person p where p.name =?1 and p.address=?2*/
List<Person> findByNameAndAddress(String name,String address);
}
  • 限制结果数量。结果数量用top和first关键字来实现。
public interface PersonRepository extends JpaRepository<Person,Long>{
 //获得符合查询条件的前10条数据
 List<Person> findFirst10ByName(String name);
//获得符合查询条件的前30条数据
 List<Person> findTop30ByName(String name);
}

(2)使用JPA的NamedQuery查询 Spring Data Jpa支持用jpa的NameQuery来定义查询方法,即一个名称映射一个查询语句。

@Entity
@NamedQuery(name="Person.FindByName", query="select p from Person p where p.name=?1")
 public class Person{
}

使用如下语句:

 public interface PersonRepository extends JpaRepository<Person,Long>{
 //使用NamedQuery里定义的查询语句,而不是根据方法名称查询
 List<Person> findByName(String name);
}

(3)使用@Query查询

  • 使用参数索引。spring data jpa还使用@Query注解再接口的方法上实现查询
public interface PersonRepository extends JpaRepository<Person,Long>{
 @Query("select p from Person p where p.address=?1")
 List<Person> findByAddress(String address);
}

  • 使用命名参数。用名称来匹配查询参数
public interface PersonRepository extends JpaRepository<Person,Long>{
 @Query("select p from Person p where p.address= :address")
 List<Person> findByAddress(@Param("address") String address);
}
  • 更新查询。spring boot jpa支持@Modifying和@Query注解组合来实现更新查询
public interface PersonRepository extends JpaRepository<Person,Long>{
 @Modifying
 @Transactional
 @Query("update Person p set p.name=?1")
/*返回值int表示更新语句影响的行数*/
 int setName(String name);
}

(4)Specification。

Jpa提供了基于准则查询的方式,即Criteria查询。而Spring Data JPA 提供了一个Specification(规范)接口,可以更方便地构造准则查询Specification接口定义了一个toPredicate方法用来构造查询条件。

  • 定义。接口类必须实现 JpaSpecificationExecutor接口
public interface PersonRepository extends JpaRepository<Person,Long>,JpaSpecificationExecutor<Person>{
        
    }

然后需要定义Criterial查询:

/*查出所有来自河南的人*/
public class CustomerSpecs{
    public static Specification<Person> personFromHenan(){
        return new Specification<Person>(){
            //通过Root来获得需要查询的属性,通过CriteriaBuilder构造查询条件
            @Override
            public Predicate toPredicate(Root<Person> root,
                                         CriteriaQuery<?> query,CriteriaBuilder cb){
                return cb.equal(root.get("address"),"河南");
            }
        };
    } 
}

注意:CriteriaBuilder,CriteriaQuery,Predicate,Root都是来自 JPA的接口。

  • 使用。静态导入:
import static com.wisely.specs.CustomerSpecs.*;

注入personRepository的Bean后:

List<Person> people = personRepository.findAll(personFromHenan());

(5)排序与分页。

考虑到排序和分页的场景,提供了Sort类和Page接口和Pageable接口。

  • 定义
public interface PersonRepository extends JpaRepository<Person,Long>{
    List<Person> findByName(String name,Sort sort);
    Page<Person> findByName(String name,Pageable pageable);
}
  • 使用排序
List<Person> people = personRepository.findByName("xxx",new Sort(Direction.ASC,"age"));
  • 使用分页
Page<Person> people1= personRepository.findByName("xxx",new PageRequest(0,10));

Page接口可以获得当前页面的记录,总页数,总记录数,是否有上一页或下一页等。

5,自定义Repository实现。

如果我们想把自己常用的数据库操作封装起来,像jparepository一样提供给我们领域类的Repository接口使用,如下:

1)定义自定义Repository接口

@NoRepositoryBean //指明当前这个接口不是我们领域类的接口
public interface CustomRepository<T,ID extends Serializable> extends PagingAndSortingRepository<T,ID>{//自定义的Repository实现PagingAndSortingRepository接口,具备分页和排序的功能
 //要定义的数据库操作方法在接口中的定义
 public void doSomething(ID id);
}

2)定义接口实现

public class CustomRepositoryImpl <T,ID extends Serializable> extends 
	SimpleJpaRepository<T,ID> implements 
	CustomRepository<T,ID>{//首先要实现CustomRepository接口,继承SimpleJpaRepository类让我们可以使用其提供的方法(如findAll)
	//让数据操作方法中可以使用entityManager
	private finall EntityManager entityManager;
	//CustomRepositoryImpl的构造函数,需要当前处理的领域类类型和entityManager作为构造函数。
	public CustomRepositoryImpl(Class<T> domainClass,EntityManager entityManager){
	super(domainClass,entityManager);
	this.entityManager=entityManager;
}
	@override
	public void doSomething(ID id){//定义数据访问操作,如调用findAll方法并构造一些查询条件
}
}

3)自定义RepositoryFactoryBean。

自定义JpaReopositoryFactoryBean替代默认的RepositoryFactoryBean,会获得一个RepositoryFactory,这个RepositoryFactory将会注册我们自定义的Repository的实现:

public class CustomRepositoryFactoryBean<T, extends JpaRepository<S, ID>, S, ID extends
        Serializable> extends JpaRepositoryFactoryBean<T, S, ID>{//自定义RepositoryFactoryBean,继承JpaRepositoryFactoryBean

@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager){
        //重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例
        return new CustomRepositoryFactory(entityManager);
        }

//创建CustomRepositoryFactory,并继承JpaRepositoryFactory
private static class CustomRepositoryFactory extends JpaRepositoryFactory {

    public CustomRepositoryFactory(EntityManager entityManager) {
        super(entityManager);
    }

    //重写 getTargetRepository方法,获得当前自定义的Repository实现
    @Override
    @SuppressWarnings({"unchecked"})
    protected <T, ID extends Serializable> SimpleJpaRepository<?, ?>
    getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
        return new CustomRepositoryFactoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager);

    }

    //重写 getRepositoryBaseClass,获得当前自定义的Repository实现的类型
    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
        return CustomRepositoryImpl.class;
    }
}
}

4开启自定义支持使用@EnableJpaRepositories的repositoryFactoryBeanClass来指定FactoryBean即可。

@EnableJpaRepositories(repositoryFactoryBeanClass=CustomRepositoryFactoryBean.class)

实例:

1,配置使用Spring Data JPA

在Spring 环境中,使用Spring Data JPA可通过@EnableJpaRepository注解来开启Spring Data JPA的支持, @EnableJpaRepository接收的value参数用来扫描数据访问层所在包下的数据访问的接口定义。

整合SpringData JPA

1)、编写一个实体类(bean)和数据表进行映射,并且配置好映射关系;

//使用JPA注解配置映射关系
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "tbl_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user;
public class User {

    @Id //这是一个主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
    private Integer id;

    @Column(name = "last_name",length = 50) //这是和数据表对应的一个列
    private String lastName;
    @Column //省略默认列名就是属性名
    private String email;

2)、编写一个Dao接口来操作实体类对应的数据表(Repository)

//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User,Integer> {
}

3)、基本的配置JpaProperties

spring:  
 jpa:
    hibernate:
#     更新或者创建数据表结构
      ddl-auto: update
#    控制台显示SQL
    show-sql: true

运行结果如下:

Snipaste_2019-11-06_00-05-53.png

测试插入数据,添加一个controller:

@RestController
public class UserController {

    @Autowired
    UserRepository userRepository;

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") Integer id){
        User user = userRepository.findOne(id);
        return user;
    }

    @GetMapping("/user")
    public User insertUser(User user){
        User save = userRepository.save(user);
        return save;
    }

}

运行,浏览器输入 http://localhost:8080/user?lastName=zanshan&email=aa

Snipaste_2019-11-06_00-11-10.png

此时控制台可以看到插入语句:

Snipaste_2019-11-06_00-12-22.png