美文网首页
Springboot集成JPA,开箱即用

Springboot集成JPA,开箱即用

作者: 孤山之王 | 来源:发表于2020-12-18 11:22 被阅读0次

    不知为什么在以前的一段时间内,我特别喜欢用 JPA ,它给我印象就是小巧灵便,为我省去了很多不必要的编码,带给我不一样的代码输出效率,因为业务在垂直划分过程中都相对来说封闭,要求在编码过程中相对来说实体关联没有那么复杂,而且项目本身的交付周期特别短,我就选择 JPA 作为我们某个特定项目的专用持久层框架,当然 JPA 自身的优势就不说啦。

    项目架构目的就是为了高质量、低成本、更便捷的交付,也是我那段时间里秉承的思路。

    下面言归正传,我们用我之前项目在springboot集成 JPA 来做一个演示。

    2.1.1. pom文件

    pom中引入 spring-boot-starter-data-jpa 依赖,注意版本。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>2.3.4.RELEASE</version>
    </dependency>
    

    2.1.2. 自定义Repository工厂类

    JPA 默认工厂类 JpaRepositoryFactoryBean ,并不能满足我们实际要求,比如我们想在插入或者修改的适合做些事情,默认工厂类就不会支持,所以我们重写一个自己的,继承 JpaRepositoryFactoryBean即可。

    JpaRepositoryFactoryBean工厂类

    继承后重写 createRepositoryFactory 方法,指定我们自己的,这里我用一个内静态类来,这个内部类 BaseRepositoryFactory 也需要继承 JpaRepositoryFactory 工厂,此处我们指定自己真实的工厂类实现 BaseRepositoryImpl

    
    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new BaseRepositoryFactory(entityManager);
    }
    
    private static class BaseRepositoryFactory<T,I extends Serializable> extends JpaRepositoryFactory{
    
            private final EntityManager em;
    
            public BaseRepositoryFactory(EntityManager em) {
                super(em);
                this.em = em;
            }
    
            @Override
            protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
                return new BaseRepositoryImpl<T, I>((Class<T>) information.getDomainType(), em);
            }
    
    
            /**
                *  设置具体的实现类的class
                * @param metadata
                * @return
                */
            @Override
            protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
                return BaseRepositoryImpl.class;
            }
    }
    

    下面就可以在我们自己的类做我们的自己事情,同时我们实现类中抽象出来一个接口暴露对外,这个接口定义我们需要共用的方法,具体实现我们额外实现。这样设计有个好处,就是耦合度降低,扩展方便。

    类之间关系图
    • 抽象接口
    @NoRepositoryBean
    @Transactional(readOnly=true,rollbackFor = Exception.class)
    public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
    
        /**
         * 根据主键删除
         *
         * @param ids
         */
        void delete(ID[] ids);
    
        /**
         *
         * @param sql
         * @return
         */
        List<Object[]> listBySQL(String sql);
    
        public Long getTargetId(String sql);
    
        /**
         *
         * @param sql
         * @param args
         */
        @Transactional(rollbackFor = Exception.class)
        void updateBySql(String sql,Object...args);
    
    
        @Transactional(rollbackFor = Exception.class)
        void updateByHql(String hql,Object...args);
    
        Page<T> findCriteria(Specification<T> spec, Pageable pageable);
    
        int batchInsert(String sql);
    
    • 实现类
    @SuppressWarnings({"unchecked"})
    public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
    
        //
        private final EntityManager entityManager;
    
        public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
            super(domainClass, em);
            this.entityManager=em;
        }
    
        @Override
        public void delete(ID[] ids) {
    
        }
    
        @Override
        public Long getTargetId(String sql) {
            Query query = entityManager.createNativeQuery(sql);
            return Long.valueOf(query.getSingleResult().toString());
        }
    
        @Override
        public void updateBySql(String sql, Object... args) {
            Query query = entityManager.createNativeQuery(sql);
            int i = 0;
            for(Object arg:args) {
                query.setParameter(++i,arg);
            }
            query.executeUpdate();
        }
    
        @Override
        public void updateByHql(String hql, Object... args) {
            Query query = entityManager.createQuery(hql);
            int i = 0;
            for(Object arg:args) {
                query.setParameter(++i,arg);
            }
            query.executeUpdate();
        }
    
        @Override
        public List<Object[]> listBySQL(String sql) {
            return entityManager.createNativeQuery(sql).getResultList();
        }
    
        @Override
        public int batchInsert(String sql) {
            Query query = entityManager.createNativeQuery(sql);
            return query.executeUpdate();
        }
    
    
        public Page<T> find(Class rootCls, CriteriaQuery<T> criteria, int pageNo, int pageSize) {
    
            //count
            CriteriaBuilder builder = entityManager.getCriteriaBuilder();
            CriteriaQuery criteriaC = builder.createQuery();
            Root root = criteriaC.from(rootCls);
            criteriaC.select(builder.count(root));
            criteriaC.where(criteria.getRestriction());
            List<Long> totals = entityManager.createQuery(criteriaC).getResultList();
            Long total = 0L;
            for (Long element : totals) {
                total += element == null ? 0 : element;
            }
            //content
            TypedQuery<T> query = entityManager.createQuery(criteria);
            query.setFirstResult((pageNo - 1) * pageSize);
            query.setMaxResults(pageSize);
    
            List<T> content = total > query.getFirstResult() ? query.getResultList() : Collections.<T> emptyList();
            Sort sort = Sort.by(Sort.Direction.DESC, Constant.DEFAULT_SORT);
            Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
            Page<T> pageRst = new PageImpl<T>(content, pageable, total);
            return pageRst;
    
        }
    
        @Override
        public Page<T> findCriteria(Specification<T> spec, Pageable pageable){
              return super.findAll(spec,pageable);
        }
    
    }
    
    

    2.1.3. 抽象实体基类

    实例基类,必须要实现一个接口 Persistable ,这个接口只定义ID主键。当然我们自己的基类也会定义这个,但是这并不冲突。

    @MappedSuperclass
    public abstract class AbsEntity<ID extends Serializable> extends AbstractEntity<ID> implements Persistable<ID> {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        public abstract ID getId();
    
        /**
         * Sets the id of the entity.
         * @param id the id to set
         */
        public abstract void setId(final ID id);
        //......
    }
    

    2.1.4. 抽象Service基类

    抽象 Service 提供基础业务类的功能。

    /** Service基类
     * @ClassName BaseService
     * @Description
     * @author WCNGS@QQ.COM
     * @Github <a>https://github.com/rothschil</a>
     * @date 20/12/18 11:05
     * @Version 1.0.0
    */
    public abstract class BaseService<T extends AbsEntity<?>, ID extends Serializable> {
    
        protected JpaRepository<T, ID> jpaRepository;
    
        public BaseService() {}
    
        /** 重要 **/
        public abstract void setJpaRepository(JpaRepository<T, ID> jpaRepository);
    
        public boolean retBoolFindByExample(T t){
            ExampleMatcher matcher = ExampleMatcher.matching();
            List<String> fields = new ArrayList<String>();
            Reflections.getField(t,fields);
            for (String fld: fields){
                matcher.withMatcher(fld,ExampleMatcher.GenericPropertyMatchers.exact());
            }
            Example<T> example = Example.of(t,matcher);
            if(jpaRepository.findAll(example).size()>0){
                return true;
            }
            return false;
        }
    
        public boolean retBoolSave(T t ){
            try{
                this.save(t);
                return true;
            }catch (RuntimeException re){
                return false;
            }
        }
    
        public List<T> findByProperty(T t,String propertyName, Object value) {
            try {
                Class<?> cls = t.getClass();
                Field[] fields = cls.getDeclaredFields();
                Method[] methods = cls.getDeclaredMethods();
                for (int i=0;i<fields.length;i++) {
                    if(fields[i].getName().equals(propertyName)){
                        String fieldSetName = StringUtils.parSetName(fields[i].getName());
                        for (Method met : methods) {
                            if (fieldSetName.equals(met.getName())) {
                                met.invoke(t,value);
                            }
                        }
                    }
                }
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NumberFormatException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return findByEntity(t);
        }
    
        public boolean retBoolDelete(T t) {
            try {
                this.delete(t);
                return true;
            } catch (RuntimeException re) {
                return false;
            }
        }
    
        public List<T> findByEntity(T t) {
            ExampleMatcher matcher = ExampleMatcher.matching();
            List<String> fields = new ArrayList<String>();
            Reflections.getField(t,fields);
            for (String fld: fields){
                matcher.withMatcher(fld,ExampleMatcher.GenericPropertyMatchers.exact());
            }
    
    /*                .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())
                    .withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())
                    .withIgnorePaths("password");*/
    
            Example<T> example = Example.of(t,matcher);
            return jpaRepository.findAll(example);
        }
    
        public Page<T> findPageByEntity(int page, int size, T t) {
            size=size==0?10:size;
            // TODO Auto-generated method stub
            Pageable pageable = PageRequest.of(page, size);
            ExampleMatcher matcher = ExampleMatcher.matching();
            List<String> fields = new ArrayList<String>();
            Reflections.getField(t,fields);
            for (String fld: fields){
                matcher.withMatcher(fld,ExampleMatcher.GenericPropertyMatchers.exact());
            }
    
    /*                .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())
                    .withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())
                    .withIgnorePaths("password");*/
    
            Example<T> example = Example.of(t,matcher);
            return jpaRepository.findAll(example,pageable);
        }
    
        /**
         * 保存单个实体
         *
         * @param t 实体
         * @return 返回保存的实体
         */
        public T save(T t) {
            return jpaRepository.save(t);
        }
    
        public T saveAndFlush(T t) {
            t = save(t);
            jpaRepository.flush();
            return t;
        }
    
    
        /**
         * 根据主键删除相应实体
         *
         * @param id 主键
         */
        public void delete(ID id) {
            jpaRepository.delete(findOne(id));
        }
    
        /**
         * 删除实体
         *
         * @param t 实体
         */
        public void delete(T t) {
            jpaRepository.delete(t);
        }
    
    
    
        /**
         * 按照主键查询
         *
         * @param id 主键
         * @return 返回id对应的实体
         */
        public T findOne(ID id) {
            return jpaRepository.getOne(id);
        }
    
    
        /**
         * 实体是否存在
         * @method      exists
         * @author      WCNGS@QQ.COM
         * @version
         * @see
         * @param id                id 主键
         * @return      boolean   存在 返回true,否则false
         * @exception
         * @date        2018/7/3 22:08
         */
        public boolean exists(ID id) {
            return findOne(id)==null?true:false;
        }
    
    
        /**
         * 统计实体总数
         * @method      count
         * @author      WCNGS@QQ.COM
         * @version
         * @see
         * @param
         * @return      long
         * @exception
         * @date        2018/7/3 22:07
         */
        public long count() {
            return jpaRepository.count();
        }
    
    
        /**
         * 查询所有实体
         * @method      findAll
         * @author      WCNGS@QQ.COM
         * @version
         * @see
         * @param
         * @return      java.utils.List<T>
         * @exception
         * @date        2018/7/3 22:07
         */
        public List<T> findAll() {
            return jpaRepository.findAll();
        }
    
        /**
         * 按照顺序查询所有实体
         * @method      findAll
         * @author      WCNGS@QQ.COM
         * @version
         * @see
         * @param sort
         * @return      java.utils.List<T>
         * @exception
         * @date        2018/7/3 22:06
         */
        public List<T> findAll(Sort sort) {
            return jpaRepository.findAll(sort);
        }
    
    
        /**
         * 分页及排序查询实体
         *
         * @param pageable 分页及排序数据
         * @return
         */
        public Page<T> findAll(Pageable pageable) {
            return jpaRepository.findAll(pageable);
        }
    
        public Page<T> findEntityNoCriteria(Integer page, Integer size) {
            Pageable pageable = PageRequest.of(page, size);
            return findAll(pageable);
        }
    
    }
    
    

    2.1.5. 如何使用?

    2.1.5.1. 继承实体基类

    lombok 一目了然,对应字段名以及列与属性对应关系,代码整体简洁。

    @EqualsAndHashCode(callSuper=false)
    @Builder(toBuilder=true)
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    @Table(name="tb_locations")
    public class Location extends AbsEntity<Long> {
    
        @Id
        @Column(name = "id", nullable = false)
        private Long id;
    
        @Column(name = "flag")
        private String flag;
    
        @Column(name = "local_code")
        //省略....
    }
    

    2.1.5.2. 继承repository基类

    直接继承就完事,方法命名的书写,有些讲究,这里就不单独说明,有兴趣童鞋自行恶补。

    public interface LocationRepository extends BaseRepository<Location, Long>,JpaSpecificationExecutor<Location> {
    
        List<Location> findByLv(int lv);
        //省略....
    }
    

    2.1.5.3. 继承Service基类

    需要引入我们 locationRepository ,这一步很关键,当然我看有的人将引入的 Repository 弄在类泛型中,效果一样。

    @Service(value="locationService")
    @Transactional(readOnly = true)
    public class LocationService extends BaseService<Location, Long> {
    
        private LocationRepository locationRepository;
    
        @Autowired
        @Qualifier("locationRepository")
        @Override
        public void setJpaRepository(JpaRepository<Location, Long> jpaRepository) {
            this.jpaRepository=jpaRepository;
            this.locationRepository =(LocationRepository)jpaRepository;
        }
    
        public List<Location> getLocationListByLevel(int lv){
            return locationRepository.findByLv(lv);
        }
    }
    
    

    2.1.5.4. 启动类

    因为我们自定义了工厂类,所以我们的启动类需要将我们工厂类引入进来,所以 repositoryFactoryBeanClass 属性需要了解下。
    这样子 springboot 整合 JPA 就完啦。

    @EnableSwagger2
    @EnableJpaAuditing
    @SpringBootApplication
    @EnableJpaRepositories(basePackages = {"xyz.wongs.drunkard"},
            repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class//Specify your own factory class
    )
    public class MoonApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MoonApplication.class,args);
        }
    
    }
    
    EnableJpaRepositories中指定工厂类

    2.1.6. 源码地址,如果觉得对你有帮助,请Star

    觉得对你有帮助,请Star

    Github源码地址

    Gitee源码地址

    相关文章

      网友评论

          本文标题:Springboot集成JPA,开箱即用

          本文链接:https://www.haomeiwen.com/subject/mmyxnktx.html