不废话直接上代码
定义接口
public interface BaseService<T> {
T update(T t);
}
然后实现
import com.feijia.pregnant.exceptions.FJException;
import org.apache.commons.beanutils.BeanMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.*;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* JPA非全量更新实现,由于动态代理的原因,必须以接口方式实现
* 建议在实际的使用过程中,接口需要继承BaseService,实现类需要继承BaseServiceImpl
* 如果已经继承的是不太容易方便更改的库文件
* 可以使用这种方式
* @Autowired
* BaseService<T> baseService;
* 然后调用.update(T)即可
* @author Connor
* 2020-03-09
* @param <T> 加了@Entity注解的数据库模型
*/
@Service
public class BaseServiceImpl<T> implements BaseService<T> {
@Autowired
EntityManager entityManager;
/**
* JPA默认更新必须加入事务管理,所以这里的@Transactional注解不能省略掉
* @param t
* @return
*/
@Override
@Transactional
public T update(T t) {
// 先把对象转map,这里会自动过滤null值
Map<String, Object> map = new BeanMap(t);
Iterator<String> iterator = map.keySet().iterator();
// 获取实体类的@Entity注解
Entity entity = t.getClass().getAnnotation(Entity.class);
if (entity == null) {
// 因为BeanMap会放置一个key为class的键值对,所以这里需要异常处理一下
throw new FJException(new NullPointerException());
}
StringBuilder stringBuilder = new StringBuilder();
// 根据注解拿到方法名,然后生成最基本的更新语句
stringBuilder.append("UPDATE ");
stringBuilder.append(entity.name());
stringBuilder.append(" ");
Field idField = null;
// 这里决定是SET还是字段
boolean flag = false;
while (iterator.hasNext()) {
String key = iterator.next();
Field field;
try {
field = t.getClass().getDeclaredField(key);
} catch (NoSuchFieldException e) {
continue;
}
if (field.getAnnotation(Id.class) == null && field.getAnnotation(Transient.class) == null) {
if (map.get(key) != null) {
if (!flag) {
// 如果是第一次,加一个SET
stringBuilder.append("SET ");
flag = true;
} else {
// 如果不是第一次,加一个逗号
stringBuilder.append(" , ");
}
// 这里拼接sql语句
stringBuilder.append(toLine(key));
stringBuilder.append(" = '");
stringBuilder.append(map.get(key).toString());
stringBuilder.append("' ");
}
} else if (field.getAnnotation(Id.class) != null) {
// 保存一下id的字段
idField = field;
}
}
// 这里直接把where放在外面,默认更新必须提供条件,避免预期之外的严重错误
stringBuilder.append(" WHERE ");
if (idField != null) {
// 拼接一下条件,这里是id,如果是别的条件,可以使用map传参或其他实现方式
stringBuilder.append(idField.getName() + " = '" + map.get(idField.getName()) + "'");
}
Query dataQuery = entityManager.createNativeQuery(stringBuilder.toString());
dataQuery.executeUpdate();
return t;
}
/**
* 驼峰 转下划线
* @param camelCase
* @return
*/
public static String toLine(String camelCase){
Pattern humpPattern = Pattern.compile("[A-Z]");
Matcher matcher = humpPattern.matcher(camelCase);
StringBuffer sb = new StringBuffer();
while(matcher.find()){
matcher.appendReplacement(sb, "_"+matcher.group(0).toLowerCase());
}
matcher.appendTail(sb);
return sb.toString();
}
}
使用也是非常简单
@Autowired
BaseService<Health> baseService;
然后就baseService.update就OK了。
也可以添加到通用接口中,那么继承的service就有这个方法了
网友评论