平时使用的持久化框架就是jpa,年前就看了一部分,对用法和原理有了一点点的理解,对做项目真的还是挺有帮助的,这里有介绍了我看过大部分的jpa的用法。好久都没有更博客了,快放假的时候就开始放纵自己了,没有看点东西,也没有锻炼,外加新年一共胖了五斤,最近有时间在的锻炼减肥,自从15年瘦下来之后,16年17年我的体重几乎没有什么改进,而且每次跑步的公里数也就5km左右,太惯着自己,最近开始10km、15km了,当然常规还是10km,不能不进步呀。
熟悉框架之前必须要知道ORM是什么,因为jpa就是一个ORM框架。
- (1)定义:ORM英文全写Object relational mapping,也就是英文的表面意思对象关系映射,关系型数据库和对象之间的一一映射,如果有个用户表user【id、userName、pwd】、如果建立对象之间的一一映射,那么我们也需要建相应的类User.class,User类的属性和数据库要一一对应,User类存在的属性,必须要在数据库表内找到相对应的属性相对应,但是数据库表中存在的属性,在类中不一定要存在,如果不对该字段进行任何的操作,可以在类中不定义对应的属性。
- (2)优势:程序中做的大部分的工作就是对输入数据做一些业务逻辑操作,目的是完成对数据的CRUD操作,最终持久化操。对数据库模型(ORM对象)的操作归根结底也就是对数据库的操作,先对ORM对象进行业务逻辑操作,然后持久化,最终完成对数据的操作。
public class SimpleJpaRepository<T, ID extends Serializable>
implements JpaRepository<T, ID>,JpaSpecificationExecutor<T>
当然也可以定义实现类的,但是只需要放在 xxxRespository一个文件下即可,我写的中impl形式的类都用来写自定义的sql,下面有介绍。jpa给我们提供了SimpleJpaRepository实现类,所以只需要我们定义接口就可以了。
extends T> S save(S var1);
extends T> Iterable save(Iterable var1);
T findOne(ID var1);
long count();
void delete(ID var1);
void delete(T var1);
void delete(Iterable extends T> var1);
void deleteAll();
@Override
public UserEntity save(UserEntity user) {
logger.info("user:[{}]", JSON.toJSON(user));
testRepository.save(user);
return user;
}
直接通过注入的xxxRespository.save即可保存,如果这个user对象存在会替换DB中的数据。delele时,如果如数要删除实体或者该条数据的唯一标识符即可。
@Query(value = "from StudentEntity s where s.s_name =?1")
StudentEntity queryByName(String name);
在定义的xxxRepository类型直接,定义方法即可,上面接口中定义的方法就是根据学生的姓名查找学生实体。
UserEntity findByNameAndPassword(String name,String password);
根据用户和密码查询用户的信息,findBy+各种条件,如果有兴趣了解详细的命名规则参考博客 http://blog.csdn.net/sbin456/article/details/53304148。
@Override
public List getScDynamicQuery(int pageSize, int size) {
Specification specification = specificationConfig.where();
Sort sort = new Sort(Sort.Direction.ASC, "id");
Pageable pageable = new PageRequest(pageSize,size, sort);
Page page = scRepository.findAll(specification,pageable);
List list = page.getContent();
if(list.size()>0){
for(ScEntity entity : list){
System.out.println(entity.getId());
}
}
return page.getContent();
}
//where条件代码 定义在SpecificationConfig类中
public Specification where(){
return new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
/**
* 1 Root ORM对象和数据表对应,
* 2 CriteriaBuilder 相当于SQL的where条件
* 3 CriteriaQuery是一个顶层的查询,它包含查询的各个部分select 、from、where、group by、order by
*/
List predicates = new ArrayList();
return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
}
};
}
1)Specification规范类,也就查询所需要的条件,归根揭底我们提供能的条件,组装了jpa能够识别的sql条件。SpecificationConfig类是一个泛型类,编译时决定Specification的类型,这样做的好处是,需要类似Specification条件的类可以进行复用。上面代码返回的Specification类型就是ScEntity的。
2)scRepository.findAll(specification,pageable)中的findAll就是JpaSpecificationExecutor接口中提供的方法,Pageable类是分页类,它提供了查询第几页,以那个属性如何排序展示结果。findAll方法的参数中Specification类型是动态变化的,也就是根据Specification的类型的变化,findAll展现出不同类型的结果,它其实是一个策略模式,我的理解策略模式跟多态性有相近的意思,运行时动态的根据对象的类型展现不同的结果。
3)下面说说这个Specification类:
public interface Specification {
Predicate toPredicate
(Root root, CriteriaQuery> query, CriteriaBuilder cb);
在我的SpecificationConfig类型,并没有实现Specification这个接口,但是我需要组装查询条件,需要返回一个Specification类型的类,接口不能初始化,所以在这里通过匿名内部类来实现这个接口。
定义查询条件:这个条件是我编的,相当于
where uuid like %uuid% and accNo =”acctNo“。
List<Predicate> list = new ArrayList<>();
String uuid = "";
list.add(cb.like(root.<String>get("uuid"), "%"+ uuid +"%" ));
String acctNo= "";
ist.add(cb.equal(root.get("acctNo"), acctNo));
return query.where(list.toArray(
new Predicate[list.size()])).getRestriction();
Root类相当于ORM对应的模型类,root是从模型中获取模型属性值的。
CriteriaBuilder:创建查询条件的。
Query:相当于一个顶层查询类,在这类相当于构建where条件的。
如果涉及到的字段比较多,而且是一些报表统计类的查询表的话,通过无论通过jpa动态查询还是通过其他的查询可能都是比较繁琐的,jpa也是支持原生sql查询的,下面提供两种查询方式,但是getResultList()返回结果有三种形式:
public List<ScEntity> findBySId(Long sId){
String sql = "select * from sc where s_id = '" + sId + "'";
Query query = entityManager.createNativeQuery(sql);
List<ScEntity> list = query.getResultList();
//List listMap = query.getResultList();
String sqlStr = "select s from ScEntity s where s.s_id = '" + sId + "'";
query = entityManager.createQuery(sqlStr) ;
List<ScEntity> listStr = query.getResultList();
return list;
}
1)createNativeQuery:参数sql 是根据数据表的名字而构造的查询sql语句。
2)createQuery:参数是以对象模型而构造的查询语句。
3) 看我注释掉的那句话,如果你是做用户报表类的查询的话,查询结果不可能是一个表内的,如果是一个表内的话你可能也只是想查询出几个属性值,那么我们的list返回结果就不是个实体类型的,而需要我们去解析,其实返回的是一个键值对,下面展示一下我做过的报表查询类的代码与解析。
Query query = entityManager.createNativeQuery(columsSql);
query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List list = query.getResultList();
//解析
Map map = new HashMap<>();
if(list.size() > 0){
for(Object o :list){
Map temp =(HashMap)o;
for (Map.Entry entry : temp.entrySet()){
if(entry.getValue() == null){
map.put(entry.getKey().toString(),BigDecimal.ZERO.toString());
}else{
map.put(entry.getKey().toString(),entry.getValue().toString());
}
}
}
}
所有的查询结果都在这个map中展示。
关联查询分为两种:一种是在动态分页中构建的,另一个是在模型对象中构建的。
1)动态分页中构建的查询关系我没有用过,所以我就不介绍了,在我参考的博客里面有。
2)另一个是在对象模型中构建的对象之间的关系,这个我随便介绍一个吧,在这之前简单的介绍一下什么是一对多,多对一,多对多,一对一的关系。一对一:一夫一妻。一对多:古代的皇帝和妃子。多对一:多个妃子都应一个皇帝。多对多:不光皇帝有多个妃子呀,王爷也有呀,两个以上的一对多关系就是多对多的关系。
/**
* 学生跟成绩是一对多的关系,一个学生有多门成绩
*/
@OneToMany(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
@JoinColumn(name="s_id", referencedColumnName="s_id",
insertable = false, updatable = false)
private List scEntities;
/**
* 一个学生对应多个老师
*/
@OneToMany(cascade = CascadeType.REMOVE,fetch = FetchType.LAZY)
@JoinColumn(name="s_id", referencedColumnName="s_id",
insertable = false, updatable = false)
private List thEntity;
通过OneToMany注解标注模型对象之间的关系,通过JoinColumn来指出对象之间通过什么关联,顺便说一下级联:级联就是相关联DB之间的同事操作,CascadeType.REMOVE只的是,如果删除当前的实体,那么跟这个实体相关的数据也会被删除,CascadeType还有很多的操作,可以自己搜一下。fetch指的是关联类的加载方式,如果在一个模型中只有一个关联关系,那么它是懒加载还是立即加载都可以,但是如果存在多个关联关系,例如上面代码中,EAGER加载机制只能立即加载一个类到内存中,其他的类均为LAZY类型的加载方式。否则会报错。
未完待续!
参考博客:http://blog.csdn.net/u010098331/article/details/51700777
https://www.cnblogs.com/dreamroute/p/5173896.html
https://www.cnblogs.com/nicuty/p/6265303.html