JavaWeb-Springboot集成JPA开箱即用

JPA

不知为什么在以前的一段时间内,我特别喜欢用 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源码地址

Gitee源码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王老邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值