spring jpa的基本操作和理解

平时使用的持久化框架就是jpa,年前就看了一部分,对用法和原理有了一点点的理解,对做项目真的还是挺有帮助的,这里有介绍了我看过大部分的jpa的用法。好久都没有更博客了,快放假的时候就开始放纵自己了,没有看点东西,也没有锻炼,外加新年一共胖了五斤,最近有时间在的锻炼减肥,自从15年瘦下来之后,16年17年我的体重几乎没有什么改进,而且每次跑步的公里数也就5km左右,太惯着自己,最近开始10km、15km了,当然常规还是10km,不能不进步呀。

ORM

熟悉框架之前必须要知道ORM是什么,因为jpa就是一个ORM框架。
- (1)定义:ORM英文全写Object relational mapping,也就是英文的表面意思对象关系映射,关系型数据库和对象之间的一一映射,如果有个用户表user【id、userName、pwd】、如果建立对象之间的一一映射,那么我们也需要建相应的类User.class,User类的属性和数据库要一一对应,User类存在的属性,必须要在数据库表内找到相对应的属性相对应,但是数据库表中存在的属性,在类中不一定要存在,如果不对该字段进行任何的操作,可以在类中不定义对应的属性。
- (2)优势:程序中做的大部分的工作就是对输入数据做一些业务逻辑操作,目的是完成对数据的CRUD操作,最终持久化操。对数据库模型(ORM对象)的操作归根结底也就是对数据库的操作,先对ORM对象进行业务逻辑操作,然后持久化,最终完成对数据的操作。

jpa基本介绍
  • (1)与hibernate的关系:jpa是标准,实际上,jpa只是定义了接口,实现都是hibernate在做。
  • (2)jpa存在的目的:让spring实现持久化操作,spring本来对第三方框架的整合上的特别的好。
  • (3)提供的功能:一般的框架都有一个对象操作DB(数据库),hibernate的session、mybatis的sqlSession,jpa的entityManager。这几个框架我都用过,仅仅一点点,也可以说说不会,当初hibernate我用的时候配置还是比较繁琐的,但是我的印象中它好像不用写sql语句。mybatis,有一个和数据相对应的mapper.xml文件,和数据库一一对应,可以写sql语句,mapper中写的sql可以进行各种if判断的,感觉也很强大的。一般的ORM框架只能提供CRUD操作,但是jpa可以提供逻辑处理的。刚看这里的时候我还不太理解这个地方是什么意思,提议的去查了一下业务逻辑的定义。对于不同的功能有这不同的实现,不同的实现,实现这些功能的核心代码就叫做业务逻辑,如果我们的功能是实现求两个数的和,那么我们程序中的代码实现也就是业务逻辑处理。
  • (4)dao:data access object数据库访问对象,跟respository是一样的。
jpa的功能
  • (1)前言:
    1)具体实现功能包括几种:直接实现JpaRepository的接口,可以通过jpa提供的方法对数据库进行操作,jap自定义接口中的@Query自定义jpql语句查询、findBy动词自定义查询语句、动态分页查询、原生sql查询。
    这里写图片描述
    2)在我定义的TeacherRepository接口中,实现了接口JpaSpecificationExecutor和
    JpaRepository。JpaRepository定义了一组标准的接口,CrudRepository继承了Repository接口,定义了一组CRUD的操作方法,也就是说只要我们的类实现JpaRepository接口就行了对模型对象进行CRUD操作。JpaSpecificationExecutor接口提供了一组基于标准JPA Criteria查询相关的方法,也就是分页动态查询相关的操作。
    3)在使用jpa的时候真的”针对接口编程“了,除了定义方法外没有做任何的实现,有的时候甚至在xxxRespository类中没有定义任何的方法,那就说明jpa提供的方法足以满足程序功能的需求了,正常接口应该需要实现类的呀,如果定义了实现类实现了
    JpaSpecificationExecutor和JpaRepository接口,那么实现类中会有几十个实现方法,实现几十个方法还怎么玩。其实jpa给我们提供的具体的实现方法不需要我们做其他的操作。
public class SimpleJpaRepository<T, ID extends Serializable> 
implements JpaRepository<T, ID>,JpaSpecificationExecutor<T>

当然也可以定义实现类的,但是只需要放在 xxxRespository一个文件下即可,我写的中impl形式的类都用来写自定义的sql,下面有介绍。jpa给我们提供了SimpleJpaRepository实现类,所以只需要我们定义接口就可以了。

(1)JPA的CRUD操作
     <S extends T> S save(S var1);
     <S extends T> Iterable<S> save(Iterable<S> 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时,如果如数要删除实体或者该条数据的唯一标识符即可。

(2)通过jpql语句进行查询
  @Query(value = "from StudentEntity s where s.s_name =?1")
    StudentEntity queryByName(String name);

在定义的xxxRepository类型直接,定义方法即可,上面接口中定义的方法就是根据学生的姓名查找学生实体。

(3)动词findBy查询
UserEntity findByNameAndPassword(String name,String password);

根据用户和密码查询用户的信息,findBy+各种条件,如果有兴趣了解详细的命名规则参考博客 http://blog.csdn.net/sbin456/article/details/53304148

(4)动态分页查询
 @Override
    public List<ScEntity> getScDynamicQuery(int pageSize, int size) {
        Specification<ScEntity> specification = specificationConfig.where();
        Sort sort = new Sort(Sort.Direction.ASC, "id");
        Pageable pageable = new PageRequest(pageSize,size, sort);
        Page<ScEntity> page = scRepository.findAll(specification,pageable);
        List<ScEntity> list = page.getContent();
        if(list.size()>0){
            for(ScEntity entity : list){
                System.out.println(entity.getId());
            }
        }
        return page.getContent();
    }
//where条件代码 定义在SpecificationConfig类中
 public Specification<T> 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<Predicate> predicates = new ArrayList<Predicate>();
                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<T> {
Predicate toPredicate
    (Root<T> 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条件的。

(5)原生查询

如果涉及到的字段比较多,而且是一些报表统计类的查询表的话,通过无论通过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<String,String> map = new HashMap<>();
            if(list.size() > 0){
                for(Object o :list){
                    Map<String,String> 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中展示。

(5)一对一、多对多查询

关联查询分为两种:一种是在动态分页中构建的,另一个是在模型对象中构建的。
1)动态分页中构建的查询关系我没有用过,所以我就不介绍了,在我参考的博客里面有。
2)另一个是在对象模型中构建的对象之间的关系,这个我随便介绍一个吧,在这之前简单的介绍一下什么是一对多,多对一,多对多,一对一的关系。一对一:一夫一妻。一对多:古代的皇帝和妃子。多对一:多个妃子都应一个皇帝。多对多:不光皇帝有多个妃子呀,王爷也有呀,两个以上的一对多关系就是多对多的关系。

 /**
     * 学生跟成绩是一对多的关系,一个学生有多门成绩
     */
    @OneToMany(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
    @JoinColumn(name="s_id", referencedColumnName="s_id",
            insertable = false, updatable = false)
    private List<ScEntity> scEntities;

    /**
     * 一个学生对应多个老师
     */
    @OneToMany(cascade = CascadeType.REMOVE,fetch = FetchType.LAZY)
    @JoinColumn(name="s_id", referencedColumnName="s_id",
            insertable = false, updatable = false)
    private List<TeacherEntity> 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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值