如题,本将要实现的功能:通过反射机制,对将要入库的实体对象的各个属性进行判断,得到最终要更新的实体对象。
应用场景:当我们需要修改数据库中某条记录时,常常需要判断新的对象(客户端修改好提交到后台的实体对象)中哪些属性是没有值的,
以免这个新的实体把原来数据库中的某些字段用null或""把数据库中的相应字段“置空”。
下面我们通过判断:
1.新的实体对象的属性值是否为空,如果某个属性值为空,保留旧的实体对象的这个属性值。
2.数据库中某条记录所对应的实体(如通过hibernate将数据库记录装入的实体对象)和新的实体对象中的属性值是否相同,如果相同保留旧的实体对象的这个属性值。
3.如果新的实体对象的属性值不为空而且与旧的实体对象相对应的属性值不同,那么就使用这个新的实体中的属性值来作为入库对象的属性值。
根据以上三点来得到新的要入库的实体对象。在下面的代码中你会发现,为了得到实体中属性值,这里使用的是属性相应的get方法而不是直接访问属性值,虽然通过Field也能得到属性值,但我认为应该充分尊重实体类的创建者对属性和方法设置的作用域,应该只访问那些实体创建者给我们暴露的方法而不是通过setAccessible来暴力访问那些隐私的属性或方法。
/**
* 对实体对象的属性值进行非空判断和一致性比较之后得到要更新的实体对象。
*
* @param beanClass 实体类类型
* @param newBean 新的实体对象
* @param oldBean 数据库中的记录对应的实体对象
* @param ignoreAttrNames 需要忽略比较新旧的属性名,它将仍然保持旧的属性值(通常是对应主键的属性)
* @return 需要入库的实体对象
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
*/
public static <T> T compareBeanAttr(Class<T> beanClass, T newBean, T oldBean, String... ignoreAttrNames)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//得到实体类的所有方法
Method[] beanMethods = beanClass.getDeclaredMethods();
//得到所有名称为: “getXxx”方法和“setXxx”方法,“Xxx”为属性名首字大写后的名称。
final String preGet = "get";
final String preSet = "set";
final List<Method> getMethods = new ArrayList<Method>();
final List<Method> setMethods = new ArrayList<Method>();
for(Method method:beanMethods){
String methodName = method.getName();
//得到get方法
if(preGet.equals(methodName.substring(0, preGet.length()))){
getMethods.add(method);
}
//得到set方法
else if(preSet.equals(methodName.substring(0, preSet.length()))){
setMethods.add(method);
}
}
/*
* 判断newBean:
* 1.newBean对应的get方法的得到的属性值是否为空(null或"")
* 2.比较newBean和oldBean对应get方法得到的属性值是否相同
*/
//判读是否需要重置数据库中列值的标记
boolean isNeedReset = false;
for(Method getMethod:getMethods){
Object newValue = getMethod.invoke(newBean);
Object oldValue = getMethod.invoke(oldBean);
//得到get方法名
String getMethodName = getMethod.getName();
//得到首字母为大写的属性名
String lastName = getMethodName.substring(preSet.length());
//把首字转成小写后称为属性名
String attrName = lastName.substring(0, 1).toLowerCase() + lastName.substring(1);
//判断方法对应的属性是否为需要忽略的属性
boolean isNeedIgnore = false;
for(String ignoreAttrName:ignoreAttrNames){
if(ignoreAttrName.equals(attrName)){
//忽略那些不需要更新的属性值
isNeedIgnore = true;
break;
}
}
if(isNeedIgnore){
//忽略这个属性的get方法,继续下一个
continue;
}
//得到对应的set方名
String setMethodName = preSet + lastName;
//判断新实体的属性值中是否有值,以及新实体和旧实体对应的属性值是否相同
if(newValue != null && !newValue.equals(oldValue)){
if(newValue instanceof String){
//如果是字符串类型的还要判断是否为""
if(!"".equals(( (String)newValue).trim() )){
//需要重置数据库中的值
isNeedReset = true;
}
else{
isNeedReset = false;
}
}else{ //其他类型的属性,可以直接存入数据库中
//需要重置数据库中的值
isNeedReset = true;
}
//判断是否要重置数据库中的值
if(isNeedReset){
//调用对应的set方法,重置数据库中的值
for(Method setMethod:setMethods){
if(setMethodName.equals(setMethod.getName())){
//调用相应的set方法
setMethod.invoke(oldBean, newValue);
}
}
//重置标记,为下次判断准备
isNeedReset = false;
}
}
}
return oldBean;
}