JavaWeb-Springboot集成JPA开箱即用

简介: Springboot集成JPA完成企业级应用,通用JPA底层能力

JPA

springboot

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

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

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

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. 自定义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);
    }

}

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);
    //......
}

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);
    }

}

5. 如何使用?

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")
    //省略....
}

5.2. 继承repository基类

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

public interface LocationRepository extends BaseRepository<Location, Long>,JpaSpecificationExecutor<Location> {

    List<Location> findByLv(int lv);
    //省略....
}

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);
    }
}

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中指定工厂类

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

觉得对你有帮助,请Star

Github源码地址

目录
相关文章
|
1月前
|
SQL Java 关系型数据库
Springboot引入jpa来管理数据库
Springboot引入jpa来管理数据库
34 0
Springboot引入jpa来管理数据库
|
1月前
|
SQL Java 数据库连接
springBoot+Jpa(hibernate)数据库基本操作
springBoot+Jpa(hibernate)数据库基本操作
39 0
|
2月前
|
Java 数据库连接 API
【Java笔记+踩坑】Spring Data JPA
从常用注解、实体类和各层编写方法入手,详细介绍JPA框架在增删改查等方面的基本用法,以及填充用户名日期、分页查询等高级用法。
【Java笔记+踩坑】Spring Data JPA
|
3月前
|
安全 Java 数据安全/隐私保护
基于SpringBoot+Spring Security+Jpa的校园图书管理系统
本文介绍了一个基于SpringBoot、Spring Security和JPA开发的校园图书管理系统,包括系统的核心控制器`LoginController`的代码实现,该控制器处理用户登录、注销、密码更新、角色管理等功能,并提供了系统初始化测试数据的方法。
58 0
基于SpringBoot+Spring Security+Jpa的校园图书管理系统
|
3月前
|
Java 关系型数据库 MySQL
|
3月前
|
Java Spring 数据库
怎样动动手指就能实现数据操作?Spring Data JPA背后的魔法揭秘
【8月更文挑战第31天】在Java开发中,数据库交互至关重要。传统的JDBC操作繁琐且难维护,而Spring Data JPA作为集成JPA的数据访问层解决方案,提供了CRUD等通用操作接口,显著减少代码量。通过继承`JpaRepository`,开发者能轻松实现数据的增删改查,甚至复杂查询和分页也不再困难。本文将通过示例详细介绍如何利用Spring Data JPA简化数据访问层的开发,提升代码质量和可维护性。
42 0
|
4月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
289 6
|
3月前
|
存储 Java 数据库
|
3月前
|
存储 Java API
|
3月前
|
Java 数据库连接 数据库
Spring Data JPA 与 Hibernate 之区别
【8月更文挑战第21天】
83 0