自动删除,我们要作的事有三件
1) 如何从要删除的Domain对象中获得需要备份的信息M
2) 这个信息M如何持久化
3) 持久化的数据如何查询。
后两者其实有多种选择,比如可以持久化到文件、数据库或者数据库+文件,随着持久化介质的不同,查询机制也不一样。这里首先讨论第一个方面。
为了作一个通用的框架,希望能通过meta data的方式简化收集信息的过程。那么就有三种方式可供选择:interface,annotation && XML,这也是目前广泛应用的各种框架如Spring,Hibernate等提供的方式。这其中,interface可以实现比较复杂的逻辑,annotation使用相对简便,而XML是最没有倾入的。手上的项目暂时不考虑移植,所以先实现了interface+annotation方式
首先来看annotation, 我定义个一个叫Record的Annotation。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <code>Record</code> is an annotation used on bean object to specify that bean can be recorded automatically at some
* stage (for example, deletion). Generally speaking, the bean can produce a <code>java.util.Map</code> and record
* itself into it. The key of that map is property name, and the value is the object gotten by getter.
* <p>
* You may use <code>Record</code> on class or method level, but any <code>Record</code> on a method which is not getter
* will be ignored. And, if an object is not marked as <code>Record</code> on class level, all <code>Record</code>
* annotations on method level will be <b>IGNORED</b> either, e.g. it will not be recorded automatically at all. So,
* make sure to use this annotation on class level.
* <p>
* You can specify <code>ignore</code> when using <code>Record</code>. On class level, <code>@Record(ignore=true)</code>
* means all getters will be ignored unless it is marked as <code>@Record</code> explicitly, vice versa. On method
* level, <code>@Record</code> impact its host only. The default value of <code>ignore</code> is false.
* <p>
* If the bean implements <code>Recordable</code>, you are able to edit the <code>java.util.Map</code> returned by
* <code>Record</code>.
*
* @author JINLO
* @see Recordable
* @since 3.1
*/
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface Record {
boolean ignore() default false;
}
以及一个叫Recordable的interface
import java.util.Map;
/**
* <code>Recordable</code> is used to specify a Object can be recorded at some stage(for example,deletion), a object can
* record itself by implements <code>void record(Map<String, Object> content)</code> function.
*
* @author JINLO
* @since 3.1
* @see Record
*/
public interface Recordable {
/**
* Write information you want to backup. This function is something like {@code toString()} function but with
* slightly difference.
* <p>
* {@code toString()} is designed for display purpose, but {@code record} is designed for backup, and the backup
* information is organized by <key,value> format. For example, when implement toString() for an {@code Item},
* ItemId may be enough for displaying:
*
* <pre>
* public String toString() {
* return "itemId=" + this.getItemId();
* }
* </pre>
*
* But for <code>Recordable</code>, you need to backup more attributes:
*
* <pre>
* public void record(Map<String, Object> content) {
* content.put("UPDATER", getUpdater().getUserID());
* content.put("CREATOR", getCreator().getUserID());
* content.put("CATEGORY", getCategory().getName());
* content.put("CATEGORY1", getCategory1().getName());
* content.put("CATEGORY2", getCategory2().getName());
* StringBuffer uomBuffer = new StringBuffer();
* for (UnitOfMeasure uom : this.getUnitOfMeasure()) {
* if (uom != null) {
* uomBuffer.append(",");
* uomBuffer.append(uom.getUom());
* }
* }
* content.put("UOM", uomBuffer.toString().substring(2));
* }
* </pre>
*
* @param writer
*/
void record(Map<String, Object> content);
}
这里说明一下,Record是希望放在Class或者Getter上的。一旦一个Class被加上Record,缺省记录所有的可读属性,除非在某个Getter上声明 Record( ignore=true)。当然也可以把Class设为Record (ignore=true),这样缺省不会记录任何属性,除非某一个Getter被声明为Record。这样对String,primitive type或boxing type,不需要太多标注就可以搞定一个Class了。
Recordable用来实现自定义抓取过程,把需删除的对象信息记录在一个Map中传给record方法。比如对于Order对象的orderItems,如果你只想记录这个Order有多少Item,而不想记录具体的Item,就可以这么写
@Record
class Order implements Recordable{
//orderNo will be recorded
public String getOrderNo(){
//..
}
@Record(ignore=true)
public List<OrderItem> getOrderItems(){
//...
}
void record(Map<String, Object> content){
content.put("itemSize", getOrderItems.size());
}
}
当然,还要有一个相应的工具类来处理annotation和interface
import java.lang.reflect.Method;
/**
* A helper class for <code>Record</code> annotation. It contains information retrieved by reflection which can be used
* to record a class. This class may be cached for performance consideration
*
* @author JINLO
* @see Record
* @since 3.1
*
*/
public class RecordDescriptor {
public static class RecordMethod {
public RecordMethod(Method readMethod, String propertyName) {
if (readMethod == null) {
throw new IllegalArgumentException("readMethod is null");
}
if (propertyName == null) {
throw new IllegalArgumentException("propertyName is null");
}
this.readMethod = readMethod;
this.propertyName = propertyName;
}
private Method readMethod;
private String propertyName;
public Method getReadMethod() {
return readMethod;
}
public String getPropertyName() {
return propertyName;
}
public int hashCode() {
return readMethod.hashCode() + 31 * propertyName.hashCode();
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null) {
return false;
}
if (o instanceof RecordMethod) {
RecordMethod target = (RecordMethod) o;
return this.readMethod.equals(target.readMethod) && this.propertyName.equals(target.propertyName);
}
return false;
}
public String toString() {
return "readMethod=" + readMethod + ",propertyName=" + propertyName;
}
}
public RecordDescriptor(Class<?> referenceClass, boolean enable, RecordMethod[] getters) {
this.referenceClass = referenceClass;
this.enable = enable;
this.getters = getters;
}
private Class<?> referenceClass;
private boolean enable;
private RecordMethod[] getters;
/**
* The reference class this <code>RecordDescriptor</code> describes
*
* @return
*/
public Class<?> getReferenceClass() {
return referenceClass;
}
/**
* Is the reference class marked <code>Record</code>
*
* @return
*/
public boolean isEnable() {
return enable;
}
/**
* Get all get methods should be recorded of reference class, including <code>Method</code> and propertyName
*
* @return empty array if no valid get method
*/
public RecordMethod[] getGetters() {
return getters;
}
}
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.oocl.wos.frm.domain.record.RecordDescriptor.RecordMethod;
import com.oocl.wos.frm.utils.BeanUtils;
/**
* Some utilities for implement <code>Recode</code>
*
* @author JINLO
* @since 3.1
*/
public class RecordUtils {
private final static RecordUtils instance = new RecordUtils();
private static Map<Class<?>, RecordDescriptor> descriptorCache = new HashMap<Class<?>, RecordDescriptor>();
private final static RecordMethod[] RECORD_METHOD_ARRAY = new RecordMethod[] {};
private final static RecordDescriptor NULL_DESCRIPTOR = new RecordDescriptor(null, false, RECORD_METHOD_ARRAY);
public final static RecordUtils instance() {
return instance;
}
private RecordUtils() {
}
/**
* Get all <code>RecordeDescriptor</code> for given class, this function will cache result for performance
* consideration.
*
* @param clz
* @return
*/
public synchronized RecordDescriptor getRecordDescriptors(Class<?> clz) {
if (clz == null) {
throw new NullPointerException("can not get read methods of class <null>");
}
RecordDescriptor result = descriptorCache.get(clz);
if (result == null) {
Record classAnnotation = clz.getAnnotation(Record.class);
if (classAnnotation != null) {
List<RecordMethod> readProperties = new ArrayList<RecordMethod>();
for (PropertyDescriptor readableProperty : BeanUtils.instance().getReadableProperties(clz)) {
Record methodAnnotation = readableProperty.getReadMethod().getAnnotation(Record.class);
boolean ignore = (methodAnnotation == null) ? classAnnotation.ignore() : methodAnnotation
.ignore();
if (!ignore) {
readProperties.add(new RecordMethod(readableProperty.getReadMethod(), readableProperty
.getName()));
}
}
result = new RecordDescriptor(clz, true, readProperties.toArray(RECORD_METHOD_ARRAY));
} else {
result = NULL_DESCRIPTOR;
}
descriptorCache.put(clz, result);
}
return result;
}
/**
* Record an object into Map in two steps:
* <ol>
* <li>If it is marked with<code>Record</code>, all valid get methods on that object will be recorded in a format
* like <property name,getter value >
* <li>Then, if it implements <code>Recordable</code>, the map filled in above setp will be passed to {@code
* Recordable.record(Map<String, Object>)} function, process in a customized way.
* </ol>
*
* @param obj
* @return null - obj is null or obj should not be recorded
*/
public Map<String, Object> toContent(Object obj) {
if (obj == null) {
return null;
}
Map<String, Object> result = null;
RecordDescriptor delDesc = getRecordDescriptors(obj.getClass());
if (delDesc.isEnable()) {
result = new HashMap<String, Object>();
for (RecordMethod getter : delDesc.getGetters()) {
String name = getter.getPropertyName();
Object value = BeanUtils.instance().executeReadMethod(obj, getter.getReadMethod());
result.put(name, value == null ? null : value.toString());
}
}
if (obj instanceof Recordable) {
Recordable dr = (Recordable) obj;
if (result == null) {
result = new HashMap<String, Object>();
}
dr.record(result);
}
return result;
}
}
RecordUtils类使用了apache的BeanUitls,并且在自己内部缓存了每个类的RecordDescriptor对象,以避免过多的反射。
至此,对于任意obj,只要调用
Map<String,Object> content=RecordUtils.toContent(obj);
就可以得到其中所有的信息了,下面所要作的就是将这些信息持久化而已,这将在下一篇描述。
采用interface+annotation的方法,我们成功的将“如何记录自己”这件事交给了对象自身。这已经有了点rich domain的意味。相对来说 rich domain意味着更好/更符合直觉的控制,对于record这种与对象自身紧密相关的行为,采用rich domain是很合适的。
分享到:
相关推荐
SQLServer 2008 自动备份数据库和定期删除数据库备份 SQLServer 2008 通过创建维护计划可以实现自动备份数据库和定期删除备份。为了实现这个功能,必须开启 SQLServer 的 SQLServer Agent 服务,因为这个服务默认是...
在SQL Server 2005中,为了确保数据的安全性和可恢复性,自动备份和自动删除备份文件是非常关键的操作。以下是如何在SQL Server 2005中实现这两个功能的详细步骤: 一、创建自动备份 1. **打开SQL Server ...
在SQL Server中,数据库的自动备份是确保数据安全和防止数据丢失的重要措施。本文将详细介绍如何设置SQL Server的自动定时备份功能,以便在无需人工干预的情况下定期执行备份任务。 首先,确保您的计算机已经安装了...
windows下oracle11g数据泵自动备份脚本含删除过期备份和使用方法 1、先建备份用的路径对象:(注:此处认为用来做备份的用户backupuser已经建好了,且已经有连接数据库的相应权限) sqlplus / as sysdba CREATE OR ...
PB 12.5 编写的自动备份工具是一款利用PowerBuilder 12.5开发的应用程序,主要用于自动化SQL Server数据库的备份过程。该工具能够按照预设的时间计划执行数据库备份,并将生成的备份文件安全地复制到网络共享位置,...
在IT领域,自动备份和按日期删除程序是保持数据安全和系统整洁的重要工具。VBScript(Visual Basic Script)是一种轻量级的脚本语言,常用于Windows系统的自动化任务,如批处理、网页交互等。本程序就是利用VBScript...
2. **自动备份策略**:在Oracle中,可以通过RMAN配置自动备份策略,例如设置每天或每周的特定时间执行备份,以及指定备份存储位置。这可以通过RMAN的`RUN`块和`BACKUP`命令实现,结合`CONFIGURE`命令设置备份参数,...
"Oracle数据库自动备份"指的是利用Oracle提供的工具和策略,设定定期自动执行数据库备份,以确保数据的安全性。 Oracle数据库提供了多种备份方式,包括物理备份和逻辑备份。物理备份主要是通过RMAN(恢复管理器)...
在IT行业中,数据库的备份是至关重要的,尤其是对于企业级应用来说,Oracle数据库的自动备份是确保数据安全的重要环节。本文将围绕“Oracle自动备份脚本”这一主题,详细讲解如何利用脚本来实现Oracle数据库的定时...
本篇文章将深入探讨如何在SQL Server 2008中设置自动备份,这对于数据库管理员以及对数据库操作有一定了解的初学者来说,是一项基础但关键的操作。 首先,我们从【描述】中了解到,SQL Server Management Studio...
标题中的“使用PB9做的sqlserver数据库的自动备份软件”指的是使用PowerBuilder 9(简称PB9)开发的一款工具,该工具专为SQL Server数据库设计,目的是实现数据库的自动化备份。PowerBuilder是一款强大的可视化编程...
excel多余对象清理工具/...删除多余对象前自动备份文件 目前删除几万个还是OK的(测试3-4万个对象处理需要2分钟左右),几十万个就另外手工处理吧,直接卡死。 还有很多不足,还有很多需要改善的地方,请大佬指点指点~
Oracle数据库是业界广泛使用的大型关系型数据库系统,其对象管理及备份与恢复机制对于数据库管理员来说至关重要。本文将详细介绍Oracle数据库的对象管理、数据类型、触发器、索引和分区策略,以及备份与恢复的相关...
Java自动备份系统是一种高效、可靠的解决方案,用于在服务器上实现资源文件的自动化上传、下载以及备份功能。这个系统的核心在于利用Java编程语言的强大能力和灵活性,结合相关库和框架,实现文件管理的自动化流程,...
本文将详细阐述如何使用PowerBuilder(PB)实现SQL数据库的自动备份,以及涉及的相关知识点,包括数据恢复、特殊数据处理和数据上传下载。 一、SQL数据库自动备份 SQL数据库自动备份是为了防止数据丢失或系统故障...
本文将围绕一款名为“文件夹自动备份工具+vb源代码”的工具进行讨论,它不仅提供了可执行的备份程序FolderBackup.exe,还附带了详细的VB源代码,让使用者能够根据自己的需求调整并学习到如何开发类似的工具。...
### SQL Server 2008 自动备份与删除策略详解 #### 一、背景与目的 随着业务数据量的增长及重要性的提升,确保数据库的安全性和稳定性成为IT部门的一项重要职责。在SQL Server 2008中,为了保护数据安全并提供有效...
Oracle数据库定时自动备份对于保障数据安全至关重要,尤其是在Windows环境下,通过批处理脚本可以实现这一功能。以下是一个详细的Oracle数据库定时自动备份的步骤和相关知识点: 1. **批处理脚本**: - `...
2. **自动备份**:该软件的核心功能是自动执行备份任务,用户可以设置定时任务,比如每天、每周或在特定时间点进行备份。这有助于防止数据丢失,尤其是在系统故障、硬件损坏或人为错误的情况下。 3. **配置文件**:...