资讯专栏INFORMATION COLUMN

Spring Boot [组件学习-Spring Data JPA]

andong777 / 2033人阅读

摘要:与的关系是什么是官方提出的持久化规范。它为开发人员提供了一种对象关联映射工具来管理应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合技术,结束现在,,等框架各自为营的局面。定义了在对数据库中的对象处理查询和事务运行时的的。

导读:

在上篇文章中对Spring MVC常用的一些注解做了简要的说明,在这篇文章中主要对Spring Data JPA 做一个简要的说明,并附有一个简单的例子,可以体会到Spring Data JPA 的强大之处。

Spring Data JPA 与JPA的关系: JPA是什么?

JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面。值得注意的是,JPA是在充分吸收了现有Hibernate,TopLink,JDO等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。
JPA定义了在对数据库中的对象处理查询和事务运行时的EntityManager的API。JPA定义一个对象级查询语言,JPQL,如果学习过Hibernate的话你可以把它看做Hibernate中的Hql语句,以允许从所述数据库中的对象的查询。
下面是JPA常用的一些解决方案:

EclipseLink (Eclipse)
Hibernate (RedHat)
Open JPA (Apache)
DataNucleus
Ebean (SourceForge)
TopLink Essentials (Glassfish)
TopLink (Oracle)
Kodo (Oracle)
Spring Data JPA:

你可以把它做做事JPA的超集 类似于 C与C++的关系,Spring Data JPA可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

也就是说Spring Data JPA 在JPA的基础上提供了更高层次的抽象,帮助我们实现了许多常用的对数据库的操作,从而提高开发效率。

Spring Data JPA 的一些常用方法

在了解Spring Data JPA之前我们首先看一下它为我们提供了那些便利。

1.简单查询:

面对简单的查询时可以通过解析方法名创建查询或使用使用 @Query 创建查询语句

比如现在我们有个方法叫做 User findByName(String name),我们可以很清楚的明白它的意思,但有没有办法让ORM框架根据方法名帮助我们推断出Sql来呢?在Spring Data JPA中这是可以的,我们只要将我们的接口继承 org.springframework.data.repository.Repository ,或者使用@RepositoryDefinition 注解进行修饰。

解析方法名创建查询 示例:
public interface UserRepository extends Repository {
    User findByName(String name)
} 


@RepositoryDefinition(domainClass = User.class, idClass = Long.class) 
public interface UserRepository{
    User findByName(String name)
} 
@Query注解查询 示例:
public interface UserRepository extends Repository {
    //对于更新操作需要添加@Modifying
    @Query("from User u where u.name=:name")
    User findUser(@Param("name") String name);
} 

当然还不止这些Spring Data JPA 还未我们提供了几个常用的Repository:

Repository: 仅仅是一个标识,没有任何方法,方便Spring自动扫描识别

CrudRepository: 继承Repository,实现了一组CRUD相关的方法

PagingAndSortingRepository: 继承CrudRepository,实现了一组分页排序相关的方法

JpaRepository: 继承PagingAndSortingRepository,实现一组JPA规范相关的方法

通过继承它们可以获得更强大的功能,当然它们之所能够运行是因为有了默认的实现类:

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository implements JpaRepository,
        JpaSpecificationExecutor {
   
}

在查询的时候,通常需要同时根据多个属性进行查询,Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:

And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);

Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);

Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);

LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);

GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);

IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();

IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();

NotNull --- 与 IsNotNull 等价;

Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);

NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);

OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);

Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);

In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

当然面对比较复杂的查询时,它们就显得无力了,如果在面对比较复杂的查询时,想要有自己的实现方法,只需要通过一个XXXImpl结尾的实现类即可使用我们自己的实现方法(比如UserRepository接口的同一个包下面建立一个普通类UserRepositoryImpl来表示该类的实现类)。

了解更多:使用 Spring Data JPA 简化 JPA 开发 - IBM

2.复杂的查询

面对复杂的查询时可以使用本地查询或JPQL或使用JPA的动态接口(JpaSpecificationExecutor)

在面对负责的条件查询时,需要有条件判断这个时候方法名推断就显得不够强大了,比如当这里有个简单的例子:“我们要根据用户的年龄,地址,性别 查询用户信息时”, 在考虑可能存在条件为空的情况下,在使用方法名推断时我们需要提供8个相应的方法,当然如果使用流的话有一个findAll方法即可,但是这无疑增大了数据库与发射生成对象压力所以并不推荐这种方式,那么在这种情况下如果可以使用判断进行条件拼接的话,便可以将查询的方法缩减到一个 这便是以上三种方式的优点(一般情况下不推荐 本地查询)。

JPQL示例:

DO:

@Entity
public class User extends AbstractPersistable {

    private String name;

    private Integer age;

    private String AddressCode;

    @Enumerated(EnumType.ORDINAL)
    private Sex sex;
    
    /**省略get/set**/
}

DAO:

public interface UserCustomRepository {

    Page search(Integer age, String AddressCode, User.Sex sex, Pageable pageRequest);

}


public interface UserRepository extends CrudRepository, UserCustomRepository {

}


public class UserRepositoryImpl implements UserCustomRepository {

    @PersistenceContext
    private EntityManager em;

    @Override
    public Page search(Integer age, String AddressCode, User.Sex sex, Pageable pageRequest) {
        String querySql = "select t ";
        String countSql = "select count(t) ";
        StringBuffer sqlBuffer = new StringBuffer("from User t where 1=1");

        if (null != age) {
            sqlBuffer.append(" and t.age = :age");
        }
        if (null != AddressCode) {
            sqlBuffer.append(" and t.AddressCode = :address");
        }
        if (null != sex) {
            sqlBuffer.append(" and t.sex = :sex");
        }

        querySql += sqlBuffer.toString();
        countSql += sqlBuffer.toString();

        Query dataQuery = em.createQuery(querySql);
        Query countQuery = em.createQuery(countSql);

        if (null != age) {
            dataQuery.setParameter("age", age);
            countQuery.setParameter("age", age);
        }
        if (null != AddressCode) {
            dataQuery.setParameter("address", AddressCode);
            countQuery.setParameter("address", AddressCode);
        }
        if (null != sex) {
            dataQuery.setParameter("sex", sex);
            countQuery.setParameter("sex", sex);
        }
        // 本地查询方式 
        // Query query = em.createNativeQuery(sql);
        // query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(User.class));
        Page page = (pageRequest == null ? new PageImpl(dataQuery.getResultList()) : this.readPage(dataQuery, countQuery, pageRequest));
        return page;
    }

    private Page readPage(Query dataQuery, Query countQuery, Pageable pageable) {
        dataQuery.setFirstResult(pageable.getOffset());
        dataQuery.setMaxResults(pageable.getPageSize());
        long totalSize = (long) countQuery.getSingleResult();
        List content = totalSize > (long) pageable.getOffset() ? dataQuery.getResultList() : Collections.emptyList();
        return new PageImpl(content, pageable, totalSize);
    }


}

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SpringBootJpaApplicationTests {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void contextLoads() {
        List userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            String name = "name" + i;
            Integer age = 19 + (i + 1) % 5;
            String addressCode = "address";
            User.Sex sex = i % 2 == 0 ? User.Sex.MAN : User.Sex.WOMAN;
            User user = new User(name, age, addressCode, sex);
            System.out.println(JSON.toJSON(user));
            userList.add(user);
        }
        userRepository.save(userList);
    }

    @Test
    public void search() {
        int page = 0;
        int size = 2;
        Pageable pageable = new PageRequest(page, size);
        Page pageUser = userRepository.search(null, null, null, pageable);
        pageUser = userRepository.search(19, null, User.Sex.WOMAN, pageable);
        System.out.println(JSON.toJSON(pageUser));
    }

}

JPA的动态接口(JpaSpecificationExecutor):

只做了简单的修改
DO:

public interface UserRepository extends CrudRepository, UserCustomRepository, JpaSpecificationExecutor {

}

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SpringBootJpaApplicationTests {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void contextLoads() {
        List userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            String name = "name" + i;
            Integer age = 19 + (i + 1) % 5;
            String addressCode = "address";
            User.Sex sex = i % 2 == 0 ? User.Sex.MAN : User.Sex.WOMAN;
            User user = new User(name, age, addressCode, sex);
            System.out.println(JSON.toJSON(user));
            userList.add(user);
        }
        userRepository.save(userList);
    }

    @Test
    public void search() {
        int page = 0;
        int size = 2;
        Pageable pageable = new PageRequest(page, size);
        Page pageUser = userRepository.search(null, null, null, pageable);
//        System.out.println(JSON.toJSON(pageUser));
        pageUser = userRepository.search(19, null, User.Sex.WOMAN, pageable);
        System.out.println(JSON.toJSON(pageUser));
//        pageUser = userRepository.search(null, null, null, pageable);
//        System.out.println(JSON.toJSON(pageUser));
//        pageUser = userRepository.search(null, null, null, pageable);
//        System.out.println(JSON.toJSON(pageUser));
    }

    @Test
    public void findAll() {
        int page = 0;
        int size = 2;
        Pageable pageable = new PageRequest(page, size);
        Page pageUser = search(19, null, User.Sex.WOMAN, pageable);
        System.out.println(JSON.toJSON(pageUser));
    }

    public Page search(final Integer age, final String AddressCode, final User.Sex sex, Pageable pageRequest) {
/*      第一种查询方式
        Specification specification = (Root root, CriteriaQuery query, CriteriaBuilder cb) -> {
            //创建查询条件
            List predicates = new ArrayList<>();
            if (age != null) {
                predicates.add(cb.equal(root.get("age"), age));
            }
            if (AddressCode != null) {
                predicates.add(cb.equal(root.get("AddressCode").as(String.class), AddressCode));
            }
            if (sex != null) {
                predicates.add(cb.equal(root.get("sex").as(User.Sex.class), sex));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

*/
        //第二种
        Specification specification = (Root root, CriteriaQuery query, CriteriaBuilder cb) -> {
            //创建查询条件
            List predicates = new ArrayList<>();
            if (age != null) {
                predicates.add(cb.equal(root.get("age"), age));
            }
            if (AddressCode != null) {
                predicates.add(cb.equal(root.get("AddressCode").as(String.class), AddressCode));
            }
            if (sex != null) {
                predicates.add(cb.equal(root.get("sex").as(User.Sex.class), sex));
            }
            Predicate[] assetTradingArray = new Predicate[predicates.size()];
            predicates.toArray(assetTradingArray);
            query.where(assetTradingArray);//这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null;即可。
            return null;
        };
        return userRepository.findAll(specification, pageRequest);
    }

}

了解更多:纯干货,Spring-data-jpa详解,全方位介绍。

Spring Data JPA 在Spring-Boot中的使用: 第一步将相关依赖导入POM文件:
    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.2.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            com.h2database
            h2
            runtime
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            com.alibaba
            fastjson
            1.2.23
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

第二步配置yml文件(使用了内存数据库因此没有配数据源):
spring:
  jpa:
    show-sql: true
    properties:
      hibernate:
        hbm2ddl:
          auto: update
第三步编写main函数:
@SpringBootApplication
public class SpringBootJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootJpaApplication.class, args);
    }
}
第四步编写相关的Repository:
public interface UserRepository extends CrudRepository, UserCustomRepository, JpaSpecificationExecutor {

}
相关逻辑编写->运行->测试->发布 学习资料收集:

教程:

JPA 使用及介绍 (我起的名字>_<)

博客介绍:

Spring ORM+Hibernate?Out!换 Spring Data JPA 吧!

纯干货,Spring-data-jpa详解,全方位介绍。 (推荐)

使用 Spring Data JPA 简化 JPA 开发

视频:

尚硅谷Spring Data视频教程

结语:

Spring Data JPA 的解析方法名创建查询非常强大,只是声明持久层的接口,相关的查询它就已经帮你去做了,让我想起一个问题框架的终点在哪?是人工智能吗?

参考文档:

全面阐释和精彩总结JPA
Spring Data 系列(二) Spring+JPA入门(集成Hibernate)
Spring Data概念【从零开始学Spring Boot】

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66782.html

相关文章

  • [直播视频] 《Java 微服务实践 - Spring Boot 系列》限时折扣

    摘要:作为微服务的基础设施之一,背靠强大的生态社区,支撑技术体系。微服务实践为系列讲座,专题直播节,时长高达小时,包括目前最流行技术,深入源码分析,授人以渔的方式,帮助初学者深入浅出地掌握,为高阶从业人员抛砖引玉。 简介 目前业界最流行的微服务架构正在或者已被各种规模的互联网公司广泛接受和认可,业已成为互联网开发人员必备技术。无论是互联网、云计算还是大数据,Java平台已成为全栈的生态体系,...

    Enlightenment 评论0 收藏0
  • 70 个 Spring 最常见面试题,Java 晋升必会

    摘要:容器自动完成装载,默认的方式是这部分重点在常用模块的使用以及的底层实现原理。 对于那些想面试高级 Java 岗位的同学来说,除了算法属于比较「天方夜谭」的题目外,剩下针对实际工作的题目就属于真正的本事了,热门技术的细节和难点成为了主要考察的内容。 这里说「天方夜谭」并不是说算法没用,不切实际,而是想说算法平时其实很少用到,甚至面试官都对自己出的算法题一知半解。 这里总结打磨了 70 道...

    Ashin 评论0 收藏0
  • Spring Boot 的简单教程(四)数据库连接之Spring Data JPA的使用

    摘要:以前都是用进行数据库的开发,最近学习之后发现显得更友好,所以我们就一起来了解一下的原理吧。简单介绍持久性是的一个规范。它用于在对象和关系数据库之间保存数据。充当面向对象的领域模型和关系数据库系统之间的桥梁。是标识出主键是指定主键的自增方式。 以前都是用Mybatis进行数据库的开发,最近学习Spring Boot之后发现JPA显得更友好,所以我们就一起来了解一下JPA的原理吧。 Spr...

    yuxue 评论0 收藏0

发表评论

0条评论

andong777

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<