SpringBoot Data JPA

1、分页查询

没有条件的分页查询:

         Repository层:

Page<Activity> findAll(Pageable pageable);

        Service层:

    // 无条件分页查询
    public Page<Activity> findHomePageMsg(int page, int size) {
        List<Activity> list = new ArrayList<Activity>();
        // 根据id来排序
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = PageRequest.of(page, size, sort);
        Page<Activity> pages = activityRepository.findAll(pageable);
        return pages;
    }

定义排序条件

查询语句中的排序是通过 Sort 类来实现的。常见的排序方式如下:

  • 按单个字段结果排序

  • 当按多个不同字段的排序结果相同时按多个字段组合排序

  • 当按多个不同字段的排序结果不同时按多个字段组合排序

下来看看如何创建实现上述条件的Sort对象

首先,创建一个按单字段排序的Sort对象。假设我们想按照Id字段的值来升序排列,实现代码如下:

new Sort(Sort.Direction.ASC, "id")

其次,创建满足场景二中的Sort对象。这里假设我们使用id 和description 为查询字段并将结果以降序排列。实现代码如下:

new Sort(Sort.Direction.DESC, "id", "description")

最后,创建满足场景三的Sort对象。这里假设降序排列description升序排列id 字段的查询结果。实现代码如下:

new Sort(Sort.Direction.DESC, "description").and(new Sort(Sort.Direction.ASC, "id"))

 

注意:page从0开始,不是从1开始

 

2、模糊查询

          百度了一下,发现还是只有自己写SQL。。。。也好,自从用了Hibernate就完全放飞自我很少自己写SQL了。语法都忘了好多,就重新从这里捡回来吧。

          在Hibernate中,自己写的SQL的格式如下↓↓↓↓(不需要接收全部字段的情况)

//  @Modifying 此注解在修改数据时使用,也就是在update和delete时使用
//  nativeQuery = true,即用原生的sql语句查询。
    @Query(value = "select a.id,u.nickName,u.portrait,a.activityName,a.startTime,a.explain,a.place,a.ticketPrice from userinfo u,activityinfo a where a.userId=u.userId ", nativeQuery = true)
    public List<Map<String,String>> test();

         后面要进行多字段的模糊查询,我本来准备写成WHERE * LIKE  “*”  OR  * LIKE  “*”   这样的,后来一想字段好像有点多啊,不能这么蠢吧,就百度发现了CONCAT关键字:

         (注意:这个CONCAT关键字有一个问题:

                     CONCAT(str1,str2,…)

                     返回值:由全体出入参数合并在一起而得到的字符串。只要输入的参数中有NULL值,就返回NULL。CONCAT允许只有一个输入参数的情况。

                     所以要用IFNULL关键字处理一下;写法如下:

SELECT*FROM (
SELECT u.userId,u.nickName,u.portrait,a.id,a.activityName,a.place,a.peopleNum,
a.startTime,a.endTime,a.ticketPrice,a.`explain`,atype.activityTypeName 
FROM userinfo u,activityinfo a,activitytype atype 
WHERE u.userId=a.userId AND a.activityTypeId=atype.id) r
WHERE CONCAT
(IFNULL(nickName,''),IFNULL(`explain`,''),IFNULL(`activityName`,''),
IFNULL(activityTypeName,''),IFNULL(place,''),IFNULL(startTime,''),IFNULL(ticketPrice,'')) LIKE "%周%"

                  实际上,LIKE后面应该直接跟?1,让字符串在Controller或者这Service层就加上前后的%拼接好,这里直接使用即可。

3、动态查询

                 我之前一直感觉hibernate好牛皮好方便,经过这次动态SQL查询。。我发现是我错了,真的是我错了。。。还不如用自己写的SQL,方便快捷。。。但是既然已经写出来了,还是做做笔记把,以后我要换教了,我要去mybatis神教了。。

                  先贴我的代码:

    public Page<Activity> conditionSearch(String str, String activityTypeId, String place, String startTime, double maxTicketPrice, double minTicketPrice, int sex, int maxAge, int minAge, int page, int size) {

        Specification querySpecifi = new Specification<Activity>() {
            @Override
            public Predicate toPredicate(Root<Activity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

                List<Predicate> predicates = new ArrayList<>();
                if (null != activityTypeId && !"".equals(activityTypeId)) {
                    // 联表的字段条件查询
                    predicates.add(criteriaBuilder.equal(root.get("activityType").get("id"), activityTypeId));
                }
                if (null != place && !"".equals(place)) {
                    predicates.add(criteriaBuilder.like(root.get("place"), "%" + place + "%"));
                }
                if (null != startTime && !"".equals(startTime)) {
                    // 动态模糊查询对时间格式需要处理!!!
                    predicates.add(criteriaBuilder.like(root.get("startTime").as(String.class), "%" + startTime + "%"));
                }
                if (0 != maxTicketPrice) {
                    predicates.add(criteriaBuilder.le(root.get("ticketPrice"), maxTicketPrice));// 小于等于
                    predicates.add(criteriaBuilder.ge(root.get("ticketPrice"), minTicketPrice));// 大于等于
                }
                if (maxTicketPrice == 0 && minTicketPrice != 0) {
                    predicates.add(criteriaBuilder.ge(root.get("ticketPrice"), minTicketPrice));
                }

                if (sex != 2) { // 不限性别
                    predicates.add(criteriaBuilder.equal(root.get("userInfo").get("sex"), sex));
                }
                if (0 != maxAge) {
                    predicates.add(criteriaBuilder.le(root.get("userInfo").get("age"), maxAge));// 小于等于
                    predicates.add(criteriaBuilder.ge(root.get("userInfo").get("age"), minAge));// 大于等于
                }
                if (maxAge == 0 && minAge != 0) {
                    predicates.add(criteriaBuilder.ge(root.get("userInfo").get("age"), minAge));
                }

                // 这下面的行代码,是因为当predicates中没有东西,也就是无条件查询时,sql最后莫名其妙会多一个WHERE 0=1,然后肯定什么都查不到啊!
                // 但是有条件的时候就不会出现这种问题,百度不到答案,只好自己在加一个相当于是废话的条件:activityId not null 加了这个,就不会出现where 0=1这样的事件了。。
                predicates.add(criteriaBuilder.isNotNull(root.get("id")));
                // 将前面的这几个动态查询用and连接,这几个条件都要符合
                Predicate ps1 = criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                predicates.clear();

                if (str != null && !"".equals(str)) {
                    // 联表模糊查询
                    predicates.add(criteriaBuilder.like(root.get("activityType").get("activityTypeName"), "%" + str + "%"));

                    predicates.add(criteriaBuilder.like(root.get("userInfo").get("nickName"), "%" + str + "%"));
                    predicates.add(criteriaBuilder.like(root.get("myExplain"), "%" + str + "%"));
                    predicates.add(criteriaBuilder.like(root.get("activityName"), "%" + str + "%"));
                    predicates.add(criteriaBuilder.like(root.get("place"), "%" + str + "%"));
                    predicates.add(criteriaBuilder.like(root.get("startTime").as(String.class), "%" + str + "%"));
                    predicates.add(criteriaBuilder.like(root.<String>get("ticketPrice").as(String.class), "%" + str + "%"));
                }

                // 这下面的行代码,是因为当predicates中没有东西,也就是无条件查询时,sql最后莫名其妙会多一个WHERE 0=1,然后肯定什么都查不到啊!
                // 但是有条件的时候就不会出现这种问题,百度不到答案,只好自己在加一个相当于是废话的条件:activityId not null 加了这个,就不会出现where 0=1这样的事件了。。
                predicates.add(criteriaBuilder.isNotNull(root.get("id")));
                // 根据关键字查询用or连接
                Predicate ps2 = criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
                predicates.clear();

                predicates.add(ps1);
                predicates.add(ps2);

                // 上面两个大条件都需要满足,使用and连接
                Predicate ps = criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));

                return ps;
            }
        };

        Pageable pageable = PageRequest.of(page, size);
        Page<Activity> result = activityRepository.findAll(querySpecifi, pageable);
        return result;
    }

                  上面的代码应该就可以看出一些端倪:(纯属个人理解,有错误的地方欢迎指正)

                                 List<Predicate>:用来存放每个动态条件

                                 Root<Activity>:你要查询的对应的实体集

                                CriteriaBuilder:确定你条件的方式,如 大于  等于  模糊查询  等于;然后最后用and关键字或者or关键字链接所有的Predicate(动态条件)

我看还有的博客是这样写的,感觉这样写比较好理解一点:

public List<UserInfo> getUserInfo(String name,int age,int high) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<UserInfo> query = cb.createQuery(UserInfo.class);

        //from
        Root<UserInfo> root = query.from(UserInfo.class);

        //where
        Predicate p1 = null;
        if(name!=null) {
            Predicate p2 = cb.equal(root.get(UserInfo_.name),name);
            if(p1 != null) {
                p1 = cb.and(p1,p2);
            } else {
                p1 = p2;
            }
        }

        if(age!=0) {
            Predicate p2 = cb.equal(root.get(UserInfo_.age), age);
            if(p1 != null) {
                p1 = cb.and(p1,p2);
            } else {
                p1 = p2;
            }
        }

        if(high!=0) {
            Predicate p2 = cb.equal(root.get(UserInfo_.high), high);
            if(p1 != null) {
                p1 = cb.and(p1,p2);
            } else {
                p1 = p2;
            }
        }
        query.where(p1);

        List<UserInfo> userInfos = em.createQuery(query).getResultList();
        return userInfos;
    }
}

作者:mtinsky
链接:https://www.jianshu.com/p/45ad65690e33
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

4、一对一、一对多、多对一、多对多

5、事务

先来了解一下@Transactional注解事务的特性吧,可以更好排查问题

1、service类标签(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式。

2、@Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。

3、默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。 
辣么什么是checked异常,什么是unchecked异常

java里面将派生于Error或者RuntimeException(比如空指针,1/0)的异常称为unchecked异常,其他继承自java.lang.Exception得异常统称为Checked Exception,如IOException、TimeoutException等

辣么再通俗一点:你写代码出现的空指针等异常,会被回滚,文件读写,网络出问题,spring就没法回滚了。然后我教大家怎么记这个,因为很多同学容易弄混,你写代码的时候有些IOException我们的编译器是能够检测到的,说以叫checked异常,你写代码的时候空指针等死检测不到的,所以叫unchecked异常。这样是不是好记一些啦

4、只读事务: 
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) 
只读标志只在事务启动时应用,否则即使配置也会被忽略。 
启动事务会增加线程开销,数据库因共享读取而锁定(具体跟数据库类型和事务隔离级别有关)。通常情况下,仅是读取数据时,不必设置只读事务而增加额外的系统开销。

--------------------------------------------------------------------------------------------------------------------------------------------

最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......  
  为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志。但是这边情况来了,当这个方法异常时候 日志是打印了,但是加的事务却没有回滚。

  例:  
   类似这样的方法不会回滚 (一个方法出错,另一个方法不会回滚) :  


if(userSave){        
    try {       
        userDao.save(user);        
        userCapabilityQuotaDao.save(capabilityQuota);       
     } catch (Exception e) {        
        logger.info("能力开通接口,开户异常,异常信息:"+e);       
     }       
 }

下面的方法回滚(一个方法出错,另一个方法会回滚):

if(userSave){       
     try {        
        userDao.save(user);        
        userCapabilityQuotaDao.save(capabilityQuota);       
       } catch (Exception e) {       
        logger.info("能力开通接口,开户异常,异常信息:"+e);        
        throw new RuntimeException();       
     }        
}

或者:  


if(userSave){        
    try {        
        userDao.save(user);        
        userCapabilityQuotaDao.save(capabilityQuota);        
    } catch (Exception e) {        
        logger.info("能力开通接口,开户异常,异常信息:"+e);        
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();       
    }       
 }

为什么不会滚呢??是对Spring的事务机制就不明白。!! 
      默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。  
      spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过  
    
配置来捕获特定的异常并回滚  
     换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
  解决方案: 
  方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
  方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值