重新对springboot进行了封装,主要有以下内容:
1、对JpaRepository进行了自定义扩展,业务类的所有数据库访问接口继承BaseDaoIF
BaseBaoFactoryBean.java
package com.mm.happy.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.persistence.EntityManager;
import java.io.Serializable;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class BaseBaoFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
public BaseBaoFactoryBean(Class<? extends R> repositoryInterface) {
super(repositoryInterface);
}
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
return new BaseRepositoryFactory(em);
}
// 创建一个内部类,该类不用在外部访问
private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private final EntityManager em;
public BaseRepositoryFactory(EntityManager em) {
super(em);
this.em = em;
}
// 设置具体的实现类是BaseRepositoryImple
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseBaoImpl<T, I>((Class<T>) information.getDomainType(), em);
}
// 设置具体的实现类的class
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseBaoImpl.class;
}
}
}
BaseBaoImpl.java
package com.mm.happy.dao;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import com.mm.happy.utils.StringUtil;
/**
*自定义扩展dao基类
*/
public class BaseBaoImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseDaoIF<T, ID> {
private final EntityManager entityManager;
// 父类没有不带参数的构造方法,这里手动构造父类
public BaseBaoImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
// 通过EntityManager来完成查询
@SuppressWarnings("unchecked")
@Override
public List<Object[]> listBySQL(String sql) {
return entityManager.createNativeQuery(sql).getResultList();
}
// 通过EntityManager来完成查询
@SuppressWarnings("unchecked")
@Override
public List listBySQL(String sql, Class<T> domainClass) {
return entityManager.createNativeQuery(sql, domainClass).getResultList();
}
@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) {
System.out.println(arg);
query.setParameter(++i, arg);
}
query.executeUpdate();
}
/**
* 组装查询条件
*/
@Override
public Specification<T> buildSpecification(Map<String, String[]> searchMap, Class<T> domainClass) {
Specification<T> specification = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// path转化
List<Predicate> predicates = new ArrayList<>();
try {
// 循环取出searchMap的值
Iterator<Entry<String, String[]>> it = searchMap.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String[]> entry = it.next();
// 值
String value = entry.getValue()[0];
// 名称
String searchfiledName = entry.getKey();
// search.xxx转换成xxx, // 获取属性的名字
String name = null;
String values[] = null;
// 有区间的时候用"search.s."起始b,"search.e."结束标识
if (searchfiledName.startsWith("search.s.")) {
values = new String[2];
name = searchfiledName.substring(9);
values[0] = value;
if (searchMap.get("search.e." + name) != null) {
values[1] = searchMap.get("search.e." + name)[0];
}
} else if (searchfiledName.startsWith("search.e.")) {
continue;// 上面已经处理,这里直接跳过
} else {
if (!StringUtil.isNotBlank(value)) {
continue;// 不是区间范围的查询,值为空直接跳过
}
name = searchfiledName.substring(7);
}
Field field = domainClass.getDeclaredField(name);
// 获取属性类型
String type = field.getGenericType().toString();
// 获取查询字段的值,
if (type.equals("class java.lang.String")) {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入值
predicates.add(cb.greaterThanOrEqualTo(root.get(name), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入值
predicates.add(cb.lessThanOrEqualTo(root.get(name), values[1]));
}
} else {// 不是区间的情况
if (searchMap.get("search.equal." + name) != null) {// 配置了精确查询的情况
predicates.add(cb.equal(root.get(name), value));
} else {// 默认为模糊查询
predicates.add(cb.like(root.get(name), "%" + value + "%"));
}
}
} else if (type.equals("class java.util.Date")) {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入时间
predicates.add(cb.greaterThanOrEqualTo(root.get(name).as(String.class), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入时间
predicates.add(cb.lessThanOrEqualTo(root.get(name).as(String.class), values[1]));
}
} else {// 不是区间的情况
predicates.add(cb.equal(root.get(name).as(String.class), value));
}
} else {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入值
predicates.add(cb.greaterThanOrEqualTo(root.get(name), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入值
predicates.add(cb.lessThanOrEqualTo(root.get(name), values[1]));
}
} else {// 不是区间的情况
predicates.add(cb.equal(root.get(name).as(String.class), value));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Predicate[] pre = new Predicate[predicates.size()];
return query.where(predicates.toArray(pre)).getRestriction();
}
};
return specification;
}
}
BaseDaoIF.java
package com.mm.happy.dao;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* dao扩展接口,子类接口需继承
* @author zbj
*
* @param <T>
* @param <ID>
*/
@NoRepositoryBean
@Transactional(readOnly=true)
public interface BaseDaoIF<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {
//sql语句查询
public List<Object[]> listBySQL(String sql);
//sql语句查询
public List<Object[]> listBySQL(String sql,Class<T> domainClass);
//sql语句更新
@Transactional
public void updateBySql(String sql,Object...args);
//sql语句更新
@Transactional
public void updateByHql(String hql,Object...args);
//组装查询条件
public Specification<T> buildSpecification(Map <String, String[]> searchMap,Class<T> domainClass);
}
2、查询条件动态组装封装,即页面上增加查询条件,后台代码无需修改
1)前端配置,页面控件的名称以"search."打头,若是区间以"search.s."和“search.e.”打头
<form id="searchForm">
<table>
<tr>
<th>姓:</th>
<td><input name="search.firstname" placeholder="精确查询"/><input type="hidden" name="search.equal.firstname" /></td>
<th>名:</th>
<td><input name="search.lastname" placeholder="默认模糊查询"/></td>
<th>id<a></a>:</th>
<td><input name="search.s.id" /></td>
<th>-</th>
<td><input name="search.e.id" /></td>
</tr>
<tr>
<th>创建时间</th>
<td><input class="easyui-datetimebox" editable="false" name="search.s.createTime" /></td>
<!--由于datebox框架上面的数据必须是时间格式的,所以我们用editable="false"来禁止用户手动输入,以免报错-->
<th>-</th>
<td><input class="easyui-datetimebox" editable="true" name="search.e.createTime" /></td>
<td><a class="easyui-linkbutton" href="javascript:void(0);" οnclick="searchFunc();" iconCls="icon-search">查找</a></td>
<td><a class="easyui-linkbutton" href="javascript:void(0);" οnclick="clearSearch();" iconCls="icon-clear">清空</a></td>
</tr>
</table>
</form>
/**
* 组装查询条件
*/
@Override
public Specification<T> buildSpecification(Map<String, String[]> searchMap, Class<T> domainClass) {
Specification<T> specification = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// path转化
List<Predicate> predicates = new ArrayList<>();
try {
// 循环取出searchMap的值
Iterator<Entry<String, String[]>> it = searchMap.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String[]> entry = it.next();
// 值
String value = entry.getValue()[0];
// 名称
String searchfiledName = entry.getKey();
// search.xxx转换成xxx, // 获取属性的名字
String name = null;
String values[] = null;
// 有区间的时候用"search.s."起始b,"search.e."结束标识
if (searchfiledName.startsWith("search.s.")) {
values = new String[2];
name = searchfiledName.substring(9);
values[0] = value;
if (searchMap.get("search.e." + name) != null) {
values[1] = searchMap.get("search.e." + name)[0];
}
} else if (searchfiledName.startsWith("search.e.")) {
continue;// 上面已经处理,这里直接跳过
} else {
if (!StringUtil.isNotBlank(value)) {
continue;// 不是区间范围的查询,值为空直接跳过
}
name = searchfiledName.substring(7);
}
Field field = domainClass.getDeclaredField(name);
// 获取属性类型
String type = field.getGenericType().toString();
// 获取查询字段的值,
if (type.equals("class java.lang.String")) {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入值
predicates.add(cb.greaterThanOrEqualTo(root.get(name), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入值
predicates.add(cb.lessThanOrEqualTo(root.get(name), values[1]));
}
} else {// 不是区间的情况
if (searchMap.get("search.equal." + name) != null) {// 配置了精确查询的情况
predicates.add(cb.equal(root.get(name), value));
} else {// 默认为模糊查询
predicates.add(cb.like(root.get(name), "%" + value + "%"));
}
}
} else if (type.equals("class java.util.Date")) {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入时间
predicates.add(cb.greaterThanOrEqualTo(root.get(name).as(String.class), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入时间
predicates.add(cb.lessThanOrEqualTo(root.get(name).as(String.class), values[1]));
}
} else {// 不是区间的情况
predicates.add(cb.equal(root.get(name).as(String.class), value));
}
} else {
if (values != null && values.length > 1) {
if (StringUtil.isNotBlank(values[0])) {
// 大于或等于传入值
predicates.add(cb.greaterThanOrEqualTo(root.get(name), values[0]));
}
if (values.length > 1 && StringUtil.isNotBlank(values[1])) {
// 小于或等于传入值
predicates.add(cb.lessThanOrEqualTo(root.get(name), values[1]));
}
} else {// 不是区间的情况
predicates.add(cb.equal(root.get(name).as(String.class), value));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Predicate[] pre = new Predicate[predicates.size()];
return query.where(predicates.toArray(pre)).getRestriction();
}
};
return specification;
}
3)在controller类中调用
/**
* 分页获取列表用户数据
* @param request
* @param response
* @param searchCondition
* @return
*/
@ResponseBody
@RequestMapping(value = "get_users", method = RequestMethod.POST)
public ResponsePage findAll(HttpServletRequest req, HttpServletResponse res) {
// 初始化设置分页对象
ResponsePage userResp = initResponsePage(req, res);
// 查询条件设置
Specification<ExampleUser> specification = userDao.buildSpecification(getSearchFields(req), ExampleUser.class);
Page<ExampleUser> userPage = userDao.findAll(specification, new PageRequest(userResp.getPageNo() - 1, userResp.getPageSize(), userResp.getSort()));
// 查询后再补充设置分页对象
userResp = setResponsePage(userResp, userPage);
return userResp;
}