不廢話直接上代碼
定義接口
public interface BaseService<T> {
T update(T t);
}
然后實(shí)現(xiàn)
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非全量更新實(shí)現(xiàn),由于動(dòng)態(tài)代理的原因岖沛,必須以接口方式實(shí)現(xiàn)
* 建議在實(shí)際的使用過程中钳降,接口需要繼承BaseService频丘,實(shí)現(xiàn)類需要繼承BaseServiceImpl
* 如果已經(jīng)繼承的是不太容易方便更改的庫(kù)文件
* 可以使用這種方式
* @Autowired
* BaseService<T> baseService;
* 然后調(diào)用.update(T)即可
* @author Connor
* 2020-03-09
* @param <T> 加了@Entity注解的數(shù)據(jù)庫(kù)模型
*/
@Service
public class BaseServiceImpl<T> implements BaseService<T> {
@Autowired
EntityManager entityManager;
/**
* JPA默認(rèn)更新必須加入事務(wù)管理蒜田,所以這里的@Transactional注解不能省略掉
* @param t
* @return
*/
@Override
@Transactional
public T update(T t) {
// 先把對(duì)象轉(zhuǎn)map凰浮,這里會(huì)自動(dòng)過濾null值
Map<String, Object> map = new BeanMap(t);
Iterator<String> iterator = map.keySet().iterator();
// 獲取實(shí)體類的@Entity注解
Entity entity = t.getClass().getAnnotation(Entity.class);
if (entity == null) {
// 因?yàn)锽eanMap會(huì)放置一個(gè)key為class的鍵值對(duì),所以這里需要異常處理一下
throw new FJException(new NullPointerException());
}
StringBuilder stringBuilder = new StringBuilder();
// 根據(jù)注解拿到方法名拙已,然后生成最基本的更新語(yǔ)句
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) {
// 如果是第一次决记,加一個(gè)SET
stringBuilder.append("SET ");
flag = true;
} else {
// 如果不是第一次,加一個(gè)逗號(hào)
stringBuilder.append(" , ");
}
// 這里拼接sql語(yǔ)句
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放在外面倍踪,默認(rèn)更新必須提供條件系宫,避免預(yù)期之外的嚴(yán)重錯(cuò)誤
stringBuilder.append(" WHERE ");
if (idField != null) {
// 拼接一下條件,這里是id建车,如果是別的條件扩借,可以使用map傳參或其他實(shí)現(xiàn)方式
stringBuilder.append(idField.getName() + " = '" + map.get(idField.getName()) + "'");
}
Query dataQuery = entityManager.createNativeQuery(stringBuilder.toString());
dataQuery.executeUpdate();
return t;
}
/**
* 駝峰 轉(zhuǎn)下劃線
* @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();
}
}
使用也是非常簡(jiǎn)單
@Autowired
BaseService<Health> baseService;
然后就baseService.update就OK了。
也可以添加到通用接口中缤至,那么繼承的service就有這個(gè)方法了