`

对象修改日志

    博客分类:
  • Java
 
阅读更多
转自:http://hi.baidu.com/419848854/blog/item/4aef20de4464785a95ee37de.html

1.对于金融业务系统中,我们常常需要记录当前用户修改了哪张表中的哪个字段以及修改前的值和修改后的值,对于业务要求较高的公司是非常必须的,这里我主要讲解以下我自己的解决方案:
我所用的框架为SPRING2+Struts2+IBATIS,主要选用IBATIS而不选用HIBERNATE的原因:方便优化SQL ,开发人员上手容易,SQL配置方便,主要还是和更新明细日志记录解决方案相照应。
前期准备工作:
1.对于要记录更新明细日志的IBATIS操作,IBATIS传入的参数必须为Java Bean,如

<statement id="updateRkItemTotal"   parameterClass="com.datadriver.risk.po.RkItemTotal">
update s_baseinfo set fcode=#fcode#,
ITCODE=#itcode#,
ITSHORTNAME=#itshortname#
where S_ID=#sid#
</statement>


2.自定义注解:
Bean类 注解

/**
* @author: zhengjianbo/Ram
* @Email: zhengjianbo2@hotmail.com
* @Company: DataDriver©2010/www.datadriver.com.cn
* @Action: 类注解接口
* @DATE: 2010-9-14-上午07:47:46
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BeanAnnotation
{

/**
* 类信息说明,主要保存该Bean对应的表名
*
* @return 注解信息
*/
public String msg();

/**
* 编辑保存日志时需要获取原有数据进行比较
*
* @return 通过返回的ibatis statement获取原有数据
*/
public String ps();

/**
* @return 返回表格
*/
public String table();
}



Bean类元素注解



/**
* @author: zhengjianbo/Ram
* @Email: zhengjianbo2@hotmail.com
* @Company: DataDriver©2010/www.datadriver.com.cn
* @Action: Bean元素注解接口
* @DATE: 2010-9-14-上午07:53:10
*/

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldAnnotation
{
/**
* 主要用于说明元素对应的字段属性名
*
* @return 元素注解说明
*/
public String name();

/**
* @return 返回字段名
*/
public String column();

}


现在比如我想对一个项目名称的更新操作记录详细的日志,则需要做以下工作:

首先,对于需要传入的JavaBean参数加入注解信息:

/**
* @author: zhengjianbo/Ram
* @Email:zhengjianbo2@hotmail.com
* @Company:DataDriver
* @Action: 指标总表实体
* @DATE: 2010-4-3-上午03:43:25
*/
@BeanAnnotation(msg="指标总表", table="S_BASEINFO", ps="common.getRkItemTotalToLog")

public class RkItemTotal implements java.io.Serializable
{
@FieldAnnotation(name="指标编码", column="ITCODE")
private String itcode;
@FieldAnnotation(name="指标简称", column="ITSHORTNAME")
private String itshortname;
private String orderstr;// 升降序字段 主要用于判断升序还是降序
@FieldAnnotation(name="基金代码", column="FCODE")
private String fcode;

private long sid;//id唯一标识符
private long fcodel;

private int actiontype; // 操作类型,只当需要记录日志的时候才使用,如果需要记录日志则该Bean必须要有该元素


其中主要需要填写的为:ps(该参数需要填写对应的IBATIS xml文件中的statement id 通过该ID,传入该Bean实体(该Bean为修改好后要传给IBATIS的参数,但其中的某个主键元素可以在PS的statement中获取该数据未修改前的状态)即可获得原有数据)如:
<statement id="getRkItemTotalLog"
    parameterClass="java.lang.Long"  --注意:如果其中只有一个参数可以使用java.lang.Long也可以为com.datadriver.risk.po.RkItemTotal,如果有超过一个参数值,则必须为Java Bean
resultClass="com.datadriver.risk.po.RkItemTotal">
SELECT  fcode,fname as itshortname
from s_baseinfo
where fcode=#sid#
</statement>



准备工作结束后,我们需要在Dao层中统一进行判断:
我将Dao层的增删改操作统一集成为一个方法:
/**
* 更新数据 包括删除添加修改 并保存操作记录以及详细步骤到数据库
*
* @param obj statement节点配置的parameterClass参数实体
* @param action xml文件中的statement的id
*
*/
void updateAttributeByPoJo(Object obj, String action){
getSqlMapClientTemplate().update(action, obj);// 执行操作
}


在service层中根据实际业务需求调用不同操作方法:
/**
* @param obj 参数实体
*/
void updateRkItemTotal(RkItemTotal obj){
commonDao.updateAttributeByPoJo(obj, "common.updateRkItemTotal");
}



有于我们的增删改统一走一个方法,这样就方便我们对所有操作类型扩展,虽然不可能扩展所有,但可以则中,如我们在Java Bean 加入的actiontype元素,我们可以在Dao方法中通过反射的方式获取Java bean中的该元素值:通过扩展updateAttributeByPoJo方法
     public void updateAttributeByPoJo(Object obj, String action)
{
int actionFlag=0;
if(obj!=null)
{
actionFlag=AnnotationUpdateType.getAction(obj);
}else
{
actionFlag=0;
}
DataDriverLog.log.info("actionFlag:"+actionFlag);
先通过反射获取actiontype元素的值,然后根据用户设定给Bean的值来区别操作,如下
/** 更新操作,只适用于更新单独的数据 */
if(actionFlag==Configer.UPDATE)
{
//annotationLog=new AnnotationUpdateLogImpl();
// annotationLog.log(getSqlMapClientTemplate(), obj); 这里用于保存更新记录的详细日志,如保存修改了表S_BASEINFO的S_TYPE字段,将内容从1 改为了2.
getSqlMapClientTemplate().update(action, obj);// 执行操作
return;
}


具体根据obj(要更新的 JavaBean)来查找更新前的数据来和该Bean进行比较,主要通过反射机制获取Bean 以及元素信息,然后通过注解方式获取需要的字段信息,表信息以及ps(获取未更新前的数据的IBATIS statementID),然后再进行比较。。。主要类如下:

/**
* @author: zhengjianbo/Ram
* @Email: zhengjianbo2@hotmail.com
* @Company: DataDriver©2010/www.datadriver.com.cn
* @Action: 用于保存类注解,实体类数据(反射获取)等元素信息
* @DATE: 2010-9-25-上午09:48:05
*/
public class AnnotationDataer
{
private Object t;
private Field[] fields;// 实体类元素数组
private Fielder[] fielders;// 实体
private Class clazz;// 当前实体类

public Fielder[] getFielders()
{
this.fields=this.getFieldByObj();
this.fielders=new Fielder[this.fields.length];
for(int i=0; i<fields.length; i++)
{
this.fielders[i]=new Fielder();
this.fielders[i].setField(fields[i]);
this.fielders[i].setFieldname(this.fields[i].getName());
this.fielders[i].setObj(this.getFieldData(fields[i]));
}
return fielders;
}

public Field[] getFieldByObj()
{
return t.getClass().getDeclaredFields();// 获得属性
}

/**
* @param field 元素
* @return 获取该元素的值
* @throws Exception 抛出异常
*/
public Object getFieldData(Field field)
{
return this.getFieldDataByName(field.getName());
}

/**
* @param fieldName 元素名称
* @return 获取元素值
*/
public Object getFieldDataByName(String fieldName){
try
{
PropertyDescriptor pd=new PropertyDescriptor(fieldName, this.getClazz());
Method getMethod=pd.getReadMethod();// 获得get方法
Object o=getMethod.invoke(t);// 执行get方法返回一个Object
DataDriverLog.log.debug("参数"+fieldName+"获取值:"+o+",值类型:");
return o;
}catch(Exception e)
{
e.printStackTrace();
return null;
}
}

/**
* @return 获取实体类注解
*/
public BeanAnnotation getBeanAnnotation()
{
return t.getClass().getAnnotation(BeanAnnotation.class);
}

public Object getT()
{
return t;
}

public void setT(Object t)
{
this.t=t;
}

public Field[] getFields()
{
return fields;
}

public void setFields(Field[] fields)
{
this.fields=fields;
}

public Class getClazz()
{
clazz=this.getT().getClass();
return clazz;
}

public void setClazz(Class clazz)
{
this.clazz=clazz;
}

public void setFielders(Fielder[] fielders)
{
this.fielders=fielders;
}

}
public void log(SqlMapClientTemplate template, Object obj)
{
List<Loger> logList=new ArrayList<Loger>();
AnnotationDataer annotationDataer=new AnnotationDataer();
annotationDataer.setT(obj);

BeanAnnotation beanAnnotation=annotationDataer.getBeanAnnotation();
String table=beanAnnotation.table();// 表名
Object oldObj=template.queryForObject(beanAnnotation.ps(), obj);// 获取数据
AnnotationDataer oldAnnotationDataer=new AnnotationDataer();
oldAnnotationDataer.setT(oldObj);

Fielder[] fielders=annotationDataer.getFielders();
/*****************************************
* 遍历要修改的数据
****************************************/
for(Fielder fielder : fielders)
{
String fieldname=fielder.getFieldname();// 字段名称(元素名称)
FieldAnnotation fieldAnnotation=fielder.getFieldAnnotation(fielder
.getField());
Object dataObj=fielder.getObj();
if(fieldAnnotation!=null)
{
Object oldDataObj=oldAnnotationDataer
.getFieldDataByName(fieldname);// 原始数据
/** 更新数据时才记录* */
if(dataObj!=null)
{
if(!(dataObj+"").equals(oldDataObj+""))
{
Loger loger=new Loger();
loger.setTable(table);
loger.setColumn(fieldAnnotation.column());
loger.setSold(oldDataObj+"");
loger.setSnew(dataObj+"");
// loger.setTuser(UserSession.get("").toString());
logList.add(loger);
}
}
}
}
分享到:
评论

相关推荐

    基于面向对象系统日志管理模块的设计与实现

    - **良好的扩展性**:通过简单地修改配置文件即可添加新的日志文件或调整日志级别,而无需改动现有代码,这为未来的需求变化提供了便利。 - **多平台兼容性**:通过使用标准C++和Boost线程库进行设计,确保了模块...

    多线程一个类对象一个日志

    在多线程环境下,如果多个线程同时访问和修改同一对象,就需要确保线程安全,防止数据不一致。 **日志管理** 日志是记录程序运行过程中的事件和异常的重要工具,有助于调试和监控程序的行为。在多线程环境中,日志...

    网络游戏-网络印刷协同排版中表格对象排版日志的构建与保护方法.zip

    本文档“网络印刷协同排版中表格对象排版日志的构建与保护方法”深入探讨了如何在这样的环境中有效地管理和保护排版过程。 首先,表格对象在印刷排版中的应用广泛,它们能够清晰地展示数据,使得信息更易理解。在...

    行业分类-设备装置-网络印刷协同排版中书眉对象排版日志的构建与保护方法.zip

    日志记录了每个书眉对象的修改历史、设置参数、时间戳等信息,便于追踪和审计。通过构建详尽的日志系统,可以及时发现并解决问题,避免错误的累积。同时,日志也有助于团队成员之间的沟通,了解各自的工作进度和修改...

    网络游戏-网络印刷协同排版中图片对象排版日志构建与保护方法.zip

    另外,引入版本控制系统如Git,可以跟踪日志的每一次更改,方便回溯和比较,降低因误操作导致的损失。 在网络印刷协同排版中,图片对象的版本控制同样重要。每个设计师在修改图片后,应该生成新的版本,并在日志中...

    VS.NET C#监听txt日志修改

    对于标题"VS.NET C#监听txt日志修改",描述提到的是如何利用Visual Studio .NET (VS.NET) 和C#语言来实时监控TXT文本日志文件的改动,并将设备操作日志数据上传到一个软件查询系统。这是一个典型的文件监控和数据...

    对象字段变化比较工具

    在实际应用中,这样的工具可以用于单元测试、代码审查、日志分析等多个场景。例如,当一个函数被调用前后,对象状态的变化可以用来验证函数是否正确执行;在持续集成环境中,可以检查每次构建后对象模型的改动,辅助...

    QT写日志模块

    这些组件可以设置为只读模式,防止用户修改日志内容。每当有新的日志信息产生时,可以使用`append()`函数添加到组件的末尾。 在`ChildTest`这个子文件夹中,可能包含了实现上述功能的示例代码,例如`main.cpp`、`...

    深入分析oracle日志文件

    LogMiner 还可以将日志中记录的信息转换为原始 SQL 语句,包括数据库的更改历史、更改类型、更改对应的 SCN 号、执行这些操作的用户信息等。 Oracle 日志文件分析可以实现以下目的: 1.査明数据库的逻辑更改; 2....

    操作日志系统设计.zip

    用户操作日志记录用户的每一步交互,如登录、浏览、修改和删除等;系统操作日志则关注后台进程、错误信息和异常处理。 设计操作日志系统时,首要任务是定义日志格式。日志应该包含足够的信息以便于分析,例如操作...

    PandaJS 使用说明(1.6):日志与 proxy 对象

    4. AOP编程:PandaJS 的Proxy支持面向切面编程(AOP),允许开发者在不修改原有代码的情况下,插入额外的功能,如添加日志、性能监控等,提高了代码的可维护性和复用性。 总结,PandaJS 的日志系统和Proxy对象提供...

    slf4j完整日志文件

    SLF4J允许直接传递异常对象给日志方法,如`logger.error("An error occurred", e);`,这会自动打印异常堆栈信息。 **8. 性能优化** 在生产环境中,应根据需求调整日志级别,减少不必要的日志输出以提升性能。 总结...

    oracle通过日志查看数据库变更情况.txt

    为了从重做日志和归档日志中提取变更信息,Oracle提供了DBMS_LOGMNR包,这是一个强大的工具,可以解析日志文件并返回数据库对象的更改记录。以下是从部分文件内容中提炼出的使用步骤: #### 步骤一:设置环境变量 ...

    mq日志介绍

    MQ日志的主要功能是记录队列管理器控制下的所有关键数据变化,包括对象的创建与删除、消息的持久化更新、事务状态、对象属性修改以及通道活动等。日志的存在使得在硬件或软件故障后能够恢复数据,防止消息丢失。 MQ...

    数据库tempdb的日志已满

    1. **事务日志溢出**:tempdb中的事务日志主要用于记录所有对tempdb进行的修改操作。如果这些操作过于频繁或单次操作产生的日志量过大,可能会迅速耗尽日志空间。 2. **日志清理机制失效**:在正常情况下,SQL ...

    PureJS (6.3):Rhino 中的日志与 proxy 对象

    5. AOP(面向切面编程):Proxy还能用于实现AOP,即在不修改原有代码的情况下,向方法中添加额外的行为,如日志记录、事务管理等。 总的来说,Rhino的Log系统提供了一套完善的日志处理机制,而Proxy对象则展示了...

    动态设置log4j的日志级别

    了解了以上方法后,我们可以创建一个简单的`changeLogLevel.jsp`页面来演示动态更改日志级别的过程。这个页面可能包含一个表单让用户选择新的日志级别,然后通过Servlet或JSP内置对象(如`request.getParameter()`)...

    ms sql 日志文件压缩

    在Microsoft SQL Server数据库管理系统中,日志文件(.ldf文件)存储了所有数据库的事务日志信息,包括数据修改、备份和恢复等操作。这些日志对于数据库的正常运行和故障恢复至关重要。然而,随着时间的推移,日志...

Global site tag (gtag.js) - Google Analytics