Spring应用框架技术笔录资料整理随笔日志

JPA Criteria查询的支持及使用.

FavoriteLoadingAdd to favorites

引言:在前段时间写有一篇”Spring and JPA的框架集成和编码测试.“的文章,其中主要介绍Spring与JPA的集成配置,并以常见的三层结构代码进行了测试。今天这篇是基于这篇文章扩展的,主要作用是,在有很多条件查询时,能够使用需要的条件进行查询,减少开发量。

如果您没有看过”Spring and JPA的框架集成和编码测试.“这篇文章,最好能去看一下,因为本篇文章是在其基础上做的扩展,示例代码也是如此。代码方面需要自己动手来操作,这样才能有更深的印象。代码定有不规范之处,也请留下您宝贵的意见,将受益匪浅。

 

准备(实体扩展和数据)

 

本文重在创建相关查询,所以先给User实体添加两个属性,用于多条件查询;同时添加几条数据,用于查询。

1. 扩展User实体

package com.stark.demo.entitys;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name=”USER”)
public class User implements Serializable {

    private static final long serialVersionUID = -6395834411937686352L;

    @Id
    @Column(name = “ID”, length = 32)
    @GeneratedValue(generator = “system-uuid”)
    @GenericGenerator(name = “system-uuid”, strategy = “uuid”)
    private String id;//唯一id,使用uuid生成策略

    @Column(name = “NAME”, length = 50)
    private String name;//用户名

    @Column(name = “PWD”, length = 50)
    private String pwd;//用户密码

    @Column(name = “STATE”, length = 2)
    private String state;//0表示禁用,1表示启用

    @Column(name = “DESCRIPTION”, length = 200)
    private String description;//描述信息

    /**    省略get和set    **/

}

2. 添加数据

spring-jpa-demo-data

 

添加Criteria查询支持(实现JpaSpecificationExecutor接口)

 

Criteria 查询:是一种类型安全和更面向对象的查询。
在Jpa中使用Criteria查询,需要在Dao层实现相应的接口JpaSpecificationExecutor。这个接口是围绕Specification接口来定义的。
Specification接口中只定义了如下一个方法:
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);该方法用于创建相关的条件。
官方该接口的API地址为:http://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html,基本的用法都已提供,还有其他的类似的接口,多看看,多学学。其他的资料可自行查找,也可以参考本文下方的参考资料。

1. 扩展UserDao

如下:

package com.stark.demo.dao;

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import com.stark.demo.entitys.User;

public interface UserDao extends CrudRepository<User, String>,JpaSpecificationExecutor<User> {

}

 

2. 添加IUserService接口方法和其实现类UserServiceImpl

(1)IUserService

此次的示例中,将原文章中的UserService接口改为IUserService接口,只是重新命名了一下,这样更加规范。
如下:

package com.stark.demo.service;

import java.util.List;
import org.springframework.data.jpa.domain.Specification;
import com.stark.demo.entitys.User;

public interface IUserService {

    /**
     * 保存User对象
     * @param vo
     * @return
     * @throws Exception
     */
    public User saveVo(User vo) throws Exception;

    /**
     * 根据id获取User对象
     * @param id
     * @return
     * @throws Exception
     */
    public User loadVo(String id) throws Exception;

    /**
     * 查询所有的User
     * @return
     * @throws Exception
     */
    public List<User> findAllList() throws Exception;

    /**
     * 使用Specification查询
     * @param spec
     * @return
     * @throws Exception
     */
    public List<User> findList(Specification<User> spec) throws Exception;

}

(2)UserServiceImpl

在此次的示例中,添加了新的查询方式,同时也修正了之前findAllList方法中返回为空的问题,具体的修改请对比一下。

package com.stark.demo.service;

import java.util.ArrayList;
import java.util.List;
import org.apache.openjpa.persistence.ReadOnly;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.stark.demo.dao.UserDao;
import com.stark.demo.entitys.User;

@Service(“userService”)
@Transactional
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserDao userDao;

    @Transactional(rollbackFor = { Exception.class })
    public User saveVo(User vo) throws Exception {
        User user = null;
        try {
            user = this.userDao.save(vo);
        } catch (Exception e) {
            System.out.println(“出错信息:” + e.getMessage());
        }
        return user;
    }

    @ReadOnly
    public User loadVo(String id) throws Exception {
        User user = null;
        try {
            user = this.userDao.findOne(id);
        } catch (Exception e) {
            System.out.println(“出错信息:” + e.getMessage());
        }
        return user;
    }

    @ReadOnly
    public List<User> findAllList() throws Exception {
        List<User> userList = new ArrayList<User>();
        try {
            userList = (List<User>) this.userDao.findAll();
        } catch (Exception e) {
            System.out.println(“出错信息:” + e.getMessage());
        }
        return userList;
    }

    @ReadOnly
    public List<User> findList(Specification<User> spec) throws Exception {
        List<User> userList = new ArrayList<User>();
        try {
            userList = this.userDao.findAll(spec);
        } catch (Exception e) {
            System.out.println(“出错信息:” + e.getMessage());
        }
        return userList;
    }

}

 

实现Specification的查询

 

上面已经继承了相关的接口,下面主要就是创建Specification了,还有Controller中的的调用实现。

1. 创建UserSpecs类,用于生成相关的Specification

如下:

package com.stark.demo.specifications;

import java.util.Map;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import com.stark.demo.entitys.User;

public class UserSpecs {

    /**
     * 设置查询条件
     * @param paramMap
     * @return
     */
    public static Specification<User> setQuery(final Map<String, Object> paramMap) {
        return new Specification<User>() {
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,
                    CriteriaBuilder builder) {
                Predicate p = null;
                //判断参数Map是否为空
                if (paramMap != null && !paramMap.isEmpty()) {
                    if(paramMap.containsKey(“key”) && paramMap.get(“key”) != null){
                        String key = “%” + paramMap.get(“key”) + “%”;
                        Path<String> _p1 = root.get(“name”);
                        Path<String> _p2 = root.get(“description”);
                        //生成对应的条件,语句为:(name LIKE key OR description LIKE key)
                        p = builder.or(builder.like(_p1, key), builder.like(_p2, key));
                    }
                }
                if(p == null){//如果以上没有相关的条件,就直接生成条件,为:(state = ‘1’)
                    p = builder.equal(root.get(“state”), “1”);
                }else{//如果已有相关的条件,就以上的条件为基础生成,为:(state = ‘1’) AND (name LIKE key OR description LIKE key)
                    p = builder.and(builder.equal(root.get(“state”), “1”), p);
                }
                if(p == null){
                    query.where();
                }else{
                    query.where(p);
                }
                return p;
            }
        };
    }

}

上面的setQuery方法,用于生成相关的条件,具体的生成请看代码上的注释。

2. Controller调用实现

添加如下方法:

/**
     * 获取指定条件的用户列表
     * @param request
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(“/userlist.do”)
    public Object userList(HttpServletRequest request) throws Exception{
        //获取参数
        String _key = request.getParameter(“key”);
        //创建存放参数的Map
        Map<String, Object> paramMap = new HashMap<String, Object>();
        //存放参数
        if(_key != null && _key.trim() != “”){
            paramMap.put(“key”, _key);
        }
        //查询数据
        List<User> userList = this.userService.findList(UserSpecs.setQuery(paramMap));
        return userList;
    }

 

测试

 

(1)不添加条件查询
在浏览器端输入:localhost:8082/stark-spring-jpa-demo/user/userlist.do
返回如下:

spring-jpa-demo-spec-1

(2)添加key值查询
在浏览器端输入:localhost:8082/stark-spring-jpa-demo/user/userlist.do?key=1
返回如下:

spring-jpa-demo-spec-2

注意:这里key值没有使用中文,因为是直接在浏览器(google)上输入的,中文会被转码,会导致查不到数据,请留意。

根据上方的两个查询即可看出条件已经生效了,最好能在控制端看看Hibernate的语句输出。

 

如有问题,欢迎指出;如需转载,请标明出处,谢谢!

 

参考资料