spring-boot中jpa使用心得

小编是从python转到java的,因此小编对python世界中的sqlalchemy和django-orm的牛逼和方便记忆有心。转到java以后,发现java世界里也有类似的工具,只不过说实话,跟python相比,确实有点弱。java中,提供数据库ORM功能的工具叫做JPA。在spring中,专门有一个项目叫做spring-data-jpa用来提供对jpa的支持。我理解jpa只是一个标准,通常使用的jpa的实现是hibernate,这就是为啥默认情况下,当在pom里引入spring-data-jpa的时候,会自动引入hiberate。

废话不多说,我们先来看看spring-data-jpa是如果简化我们的开发的,请看以下代码。这段代码中,我们只需定义一个扩展自JpaRepository的接口,而在该接口中,我们只需要按照spring-data-jpa给定的规则来生成一个函数声明即可。例如该接口中,findById就等于原生的SQL : select * from data_center_info where id = ?

public interface DataCenterInfoDao extends JpaRepository<DataCenterInfo, Long>,
        JpaSpecificationExecutor<DataCenterInfo> {
    /**
     * Find by id optional.
     *
     * @param id the id
     * @return the optional
     */
    Optional findById(Long id);
}

一开始感觉这种方式还是挺方便的,但是用着用着发现,这样的方式功能有点不健全的,例如:

  1. 无法实现join操作
  2. 只能返回List 或者 DataCenterInfo 这样的对象,如果我想返回对象中某个字段呢? 瞎了……
  3. 函数的名字一长,真的让人有点晕乎啊

对于上述的问题,jpa也提供了原生的SQL方式来弥补这样的问题,例如:

public interface DomainRecordHistoryDao extends JpaRepository<DomainRecordHistory, Serializable> {

    List findByDomainNameAndEnterpriseIdAndCreateTime(String domainName, String enterpriseId, Date date);

    @Query(value = "select distinct(create_time) from domain_record_history where domain_name = ?1 and enterprise_id=?2 order by create_time ASC ", nativeQuery = true)
    List findCreateTimeByDomainNameAndEnterpriseId(String domainName, String enterpriseId);
}

但是,既然已经用了ORM的方式干掉SQL了,为啥我要倒退回去重写SQL,我实在不能接受原生的这种SQL写法。功夫不负有心人,翻了不少的书和网页,终于让我找到更好的方式,并且在《spring实战(第四版)》中也有提到,书中将接下来我要提到的这种方式,称之为混合自定义的功能。
具体来说,当spring-data-jpa为Repository接口生产实现的时候,它还会查找名字与接口相同,并且添加了Impl后缀的一个类。如果这个类存在的话,spring-data-jpa将会把它的方法与spring-data-jpa所生成的方法合并在一起。对于上述DataCenterInfoDao接口而言,要查找的类名就是DataCenterInfoDaoImpl

我首先定义了如下的接口:

     public interface DataCenterInfoAddition {
        List countDataCenterInfoByArea(Province belongProvince);
     }

紧接着,我修改一下之前定义的DataCenterInfoDao:

public interface DataCenterInfoDao extends JpaRepository<DataCenterInfo, Long>,
        JpaSpecificationExecutor<DataCenterInfo>, DataCenterInfoAddition {
    /**
     * Find by id optional.
     *
     * @param id the id
     * @return the optional
     */
    Optional findById(Long id);
}

最后我定义一个DataCenterInfoDaoImpl实现类,用于实现DataCenterInfoAddition。在这个实现中,我直接使用JPA Criteria API来实现对应的数据库功能。countDataCenterInfoByArea中实现的功能无法直接使用jpa定义函数名的方式来实现,这个函数返回值有两个,一个是表的province字段以及它对应的数目

public class DataCenterInfoDaoImpl implements DataCenterInfoAddition {

    @PersistenceContext
    private EntityManager em;


    @Override
    public List countDataCenterInfoByArea(Province belongProvince) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery(Tuple.class);
        Root nnInfo = cq.from(DataCenterInfo.class);

        cq.multiselect(nnInfo.get("province"), cb.count(nnInfo)).groupBy(nnInfo.get("province"))
                .orderBy(cb.desc(cb.count(nnInfo)));
        if (belongProvince != null && !belongProvince.getName()
                .equals(ProvinceEnum.Jituan.getName()) && !belongProvince.getName()
                .equals(ProvinceEnum.Quanguo.getName())) {
            cq.where(cb.equal(nnInfo.get("belongProvince"), belongProvince));
        }
        return em.createQuery(cq).getResultList();
    }

你可能感兴趣的:(java,spring-boot,spring-data-jpa)