用java自定义orm框架实现数据持久化

Posted by Blog of Tech@Yunlaiwu.com on March 27, 2017

用java自定义ORM框架实现数据持久化


1. ORM介绍:

对象关系映射(Object Relational Mapping),简称ORM。这是一种程序技术,把面向对象编程中对象模型与关系型数据库里的表记录进行映射。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。

2. 使用背景:

假如一款应用需要每时每刻都对用户的数据进行频繁的操作,那么每次用户的业务行为都会往数据库生成一条执行语句的话,那么数据库很快就会进入到瓶颈。正常的情况是用户操作只会影响内存里的缓存数据,将需要修改的实体对象(Entity)放到一个生产者队列里,然后通过专门的消费者线程异步执行sql操作。 cmd-markdown-logo

3. 包介绍

cmd-markdown-logo

3. 主要类文件介绍

  1. Cacheable抽象类主要是对需要持久化的对象的一种抽象,是对象各种db状态的转换。 ```java package com.yunlaiwu.cache;

public abstract class Cacheable {

/** 当前实体对象的db状态 */
protected DbStatus status;

public abstract DbStatus getStatus();

public abstract boolean isInsert();

public abstract boolean isUpdate();

public abstract boolean isDelete();

public abstract void setInsert();

public abstract void setUpdate();

public abstract void setDelete();

/**
 * 进行数据操作
 */
public abstract void save();

}


2.AbstractCacheable是对Cacheable的骨架实现,对各种抽象方法提供默认的实现

```java
package com.yunlaiwu.cache;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yunlaiwu.utils.DbUtils;
import com.yunlaiwu.utils.SqlUtils;

public abstract class AbstractCacheable extends Cacheable {
	
	private static Logger logger = LoggerFactory.getLogger(AbstractCacheable.class); 

	@Override
	public DbStatus getStatus() {
		return this.status;
	}

	@Override
	public final boolean isInsert() {
		return this.status == DbStatus.INSERT;
	}

	@Override
	public final boolean isUpdate() {
		return this.status == DbStatus.UPDATE;
	}

	@Override
	public final boolean isDelete() {
		return this.status == DbStatus.DELETE;
	}

	public void setInsert() {
		this.status = DbStatus.INSERT;
	}

	public final void setUpdate(){
		this.status = DbStatus.UPDATE;
	}

	public final void setDelete(){
		this.status = DbStatus.DELETE;
	}
	
	public final void save() {
		String saveSql = SqlUtils.getSaveSql(this);
		if (DbUtils.executeSql(saveSql)) {
			this.status = DbStatus.NORMAL;
		}
		if (logger.isDebugEnabled()) {
			System.err.println(saveSql);
		}
	}
}

  1. DbStatus表示各种db状态 ```java package com.yunlaiwu.cache;

public enum DbStatus {

/** 无需入库 */
NORMAL,
/** 需要更新 */
UPDATE,
/** 需要插入 */
INSERT,
/** 需要删除 */
DELETE,
; }

4. 采用注解来进行orm映射,注解命名借鉴了hibernate的命名,分别有:
```java
package com.yunlaiwu.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** 
 * 
 *  标识该对象需要持久化
 */
@Documented
@Target(ElementType.TYPE) // 只能用来描述类、接口(包括注解类型) enum声明
@Retention(RetentionPolicy.RUNTIME) // 程序运行时执行
public @interface Entity {
	
	/** 不为空则说明表名字跟entity类名字不一致 */
	String table() default "";
}

package com.yunlaiwu.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** 
 * 标识该属性需要映射到表字段
 */
@Documented
@Target(ElementType.FIELD) // 只能用来描述类中的Field
@Retention(RetentionPolicy.RUNTIME) // 运行时执行
public @interface Column {
	
	/** 不为空则说明表字段名字跟entity属性不一致 */
	String name() default "";
}

package com.yunlaiwu.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *  查询得到实体的唯一索引字段
 *  每一个实体至少需要一个Id字段
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {

}

5.将对象与表记录进行映射的orm桥梁(OrmBridge.Java)

package com.yunlaiwu.orm;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/** 详细的将entityBean与表之间进行映射的一个类, 可以理解成这个类包含了表和bean所有的信息,并且一一对应 **/
public class OrmBridge {
	/** 对应的数据库表名称 */
	private String tableName;
	/** 缓存所有表字段及其对应的getter method */
	private Map<String, Method> getterMap = new HashMap<>();
	/** 缓存所有表字段及其对应的setter method */
	private Map<String, Method> setterMap = new HashMap<>();
	/** 被覆写的property与表column的映射 */
	private Map<String, String> propertyToColumnOverride = new HashMap<>();
	/** 被覆写的表column与property的映射 */
	private Map<String, String> columnToPropertyOverride = new HashMap<>();
	/** 实体所有的主键字段 */
	private Set<String> uniqueProperties = new HashSet<>();
	/** 需要持久化的字段 */
	private Set<String> properties = new HashSet<>();
	
	public String getTableName() {
		return tableName;
	}
	
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public Map<String, Method> getGetterMap() {
		return getterMap;
	}

	public void addGetterMethod(String field, Method method) {
		this.getterMap.put(field, method);
	}

	public Map<String, Method> getSetterMap() {
		return setterMap;
	}

	public void addSetterMethod(String field, Method method) {
		this.setterMap.put(field, method);
	}

	/** 
	 * 返回查询实体的id组合
	 * @return
	 */
	public List<String> getQueryProperties() {
		return new ArrayList<>(this.uniqueProperties);
	}

	public Method getGetterMethod(String field) {
		return this.getterMap.get(field);
	}
	
	public void addUniqueKey(String id) {
		this.uniqueProperties.add(id);
	}
	
	public void addProperty(String property) {
		this.properties.add(property);
	}
	
	public void addPropertyColumnOverride(String property, String column) {
		this.propertyToColumnOverride.put(property, column);
		this.columnToPropertyOverride.put(column, property);
	}
	
	public String getOverrideProperty(String property) {
		return this.propertyToColumnOverride.get(property);
	}
	
	public Map<String, String> getPropertyToColumnOverride() {
		return propertyToColumnOverride;
	}

	public Map<String, String> getColumnToPropertyOverride() {
		return columnToPropertyOverride;
	}

	public void setColumnToPropertyOverride(Map<String, String> columnToPropertyOverride) {
		this.columnToPropertyOverride = columnToPropertyOverride;
	}

	public void setPropertyToColumnOverride(Map<String, String> propertyToColumnOverride) {
		this.propertyToColumnOverride = propertyToColumnOverride;
	}

	public List<String> listProperties() {
		return new ArrayList<>(this.properties);
	}

}

6.OrmProcessor类缓存各种entity与表的映射关系

package com.yunlaiwu.orm;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.yunlaiwu.annotation.Column;
import com.yunlaiwu.annotation.Entity;
import com.yunlaiwu.annotation.Id;
import com.yunlaiwu.exception.OrmConfigExcpetion;
import com.yunlaiwu.utils.ClassFilter;
import com.yunlaiwu.utils.ClassScanner;
import com.yunlaiwu.utils.StringUtils;


/** 用枚举项当做一个持久化对象,进行缓存entityBean和table的关系(Map) **/
public enum OrmProcessor {

	INSTANCE;
	
	/** entity与对应的ormbridge的映射关系 */
	private Map<Class<?>, OrmBridge> classOrmMapperr = new HashMap<>();



	/** 拿到所有entity类并创建对应的bridge对象,存到Mapper中 **/
	public void initOrmBridges() {
		Set<Class<?>> entityClazzs = listEntityClazzs();
		
		for (Class<?> clazz:entityClazzs) {
			OrmBridge bridge = createBridge(clazz);
			this.classOrmMapperr.put(clazz, bridge);
		}
	}

	/** 使用反射的方式,动态操作OrmBridges对象 **/
	private OrmBridge createBridge(Class<?> clazz) {
		OrmBridge bridge = new OrmBridge();
		//拿到class的Entity注解对象
		Entity entity = (Entity) clazz.getAnnotation(Entity.class);
		//没有设置tablename,则用entity名首字母小写
		if (entity.table().length() <= 0) {
			bridge.setTableName(StringUtils.firstLetterToLowerCase(clazz.getSimpleName()));
		}else {
			bridge.setTableName(entity.table());
		}
		//获取该类所有声明的字段,包括public、private和proteced,但是不包括父类的字段
		Field[] fields = clazz.getDeclaredFields();
		for (Field field:fields) {
			//拿到field的Column注解对象
			Column column = field.getAnnotation(Column.class);

			//拿到field声明时的名字
			String fieldName = field.getName();
			try{
				//如果声明field有Column注解,那么拿到对应getter,setter方法,并存放到bridge对象的getterMethor(HashMap)中
				if (column != null) {
					Method m = clazz.getMethod("get" + StringUtils.firstLetterToUpperCase(field.getName()));
					bridge.addGetterMethod(fieldName, m);
					Method m2 = clazz.getMethod("set" + StringUtils.firstLetterToUpperCase(field.getName()), field.getType());
					bridge.addSetterMethod(fieldName, m2);
				}
				//如果field上有主键注解的话,那么放到bridge对象中的主键集合uniqueProperties(Set)里
				if (field.getAnnotation(Id.class) != null) {
					bridge.addUniqueKey(fieldName);
				}
				//如果column注解的name属性非空,则加入到bridge对象的property与表column的映射集合(Map)中.
				if (!StringUtils.isEmpty(column.name())) {
					bridge.addPropertyColumnOverride(fieldName, column.name());
				}
				//把entity的声明的字段名添加到bridge的持久化字段的集合(Set)里
				bridge.addProperty(fieldName);
			}catch(Exception e) {
				throw new OrmConfigExcpetion(e);
			}
			// 如果没有唯一索引,那么抛出异常
			if (bridge.getQueryProperties().size() <= 0) {
				throw new OrmConfigExcpetion(clazz.getSimpleName() + " entity 没有查询索引主键字段");
			}
		}
		
		return bridge;
	}

	/** 读取包下面所有带有Entity注解的class **/
	private Set<Class<?>> listEntityClazzs() {
		return ClassScanner.getClasses("com.yunlaiwu.entity",
				/**使用内部类实现ClassFilter接口,并重写accept方法,**/
				new ClassFilter() {
					@Override
					public boolean accept(Class<?> clazz) {return clazz.getAnnotation(Entity.class) != null;}
				});
	}
	
	public OrmBridge getOrmBridge(Class<?> clazz) {
		return this.classOrmMapperr.get(clazz);
	}
}

7.BeanProcessor类主要是用于将jdbc的ResultSet反射为对应的entity实体,这个类是从apache dbutils框架拿来用的

package com.yunlaiwu.orm;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;  
import java.sql.ResultSetMetaData;  
import java.sql.SQLException;  
import java.sql.SQLXML;  
import java.sql.Time;  
import java.sql.Timestamp;  
import java.util.ArrayList;  
import java.util.Arrays;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  

/** BeanProcessor类主要是用于将jdbc的ResultSet反射为对应的entity对象,这个类是从apache dbutils框架拿来用的 **/
public class BeanProcessor  
{  
    protected static final int PROPERTY_NOT_FOUND = -1;  
    private static final Map<Class<?>, Object> primitiveDefaults = new HashMap();  
    private final Map<String, String> columnToPropertyOverrides;  
  
    static  
    {  
        primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));  
        primitiveDefaults.put(Short.TYPE, Short.valueOf((short)0));  
        primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte)0));  
        primitiveDefaults.put(Float.TYPE, Float.valueOf(0.0F));  
        primitiveDefaults.put(Double.TYPE, Double.valueOf(0.0D));  
        primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));  
        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);  
        primitiveDefaults.put(Character.TYPE, Character.valueOf('\000'));  
    }  
  
    public BeanProcessor()  
    {  
        this(new HashMap());  
    }  
  
    public BeanProcessor(Map<String, String> columnToPropertyOverrides)  
    {  
        if (columnToPropertyOverrides == null) {  
            throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");  
        }  
        this.columnToPropertyOverrides = columnToPropertyOverrides;  
    }  
  
    public <T> T toBean(ResultSet rs, Class<T> type)  
            throws SQLException  
    {  
        PropertyDescriptor[] props = propertyDescriptors(type);  

        ResultSetMetaData rsmd = rs.getMetaData();
        int[] columnToProperty = mapColumnsToProperties(rsmd, props);

        return createBean(rs, type, props, columnToProperty);
    }  
  
    public <T> List<T> toBeanList(ResultSet rs, Class<T> type)  
            throws SQLException  
    {  
        List<T> results = new ArrayList();  
        if (!rs.next()) {  
            return results;  
        }  
        PropertyDescriptor[] props = propertyDescriptors(type);  
        ResultSetMetaData rsmd = rs.getMetaData();  
        int[] columnToProperty = mapColumnsToProperties(rsmd, props);  
        do  
        {  
            results.add(createBean(rs, type, props, columnToProperty));  
        } while (rs.next());  
        return results;  
    }  
  
    private <T> T createBean(ResultSet rs, Class<T> type, PropertyDescriptor[] props, int[] columnToProperty)  
            throws SQLException  
    {  
        T bean = newInstance(type);  
        for (int i = 1; i < columnToProperty.length; i++) {  
            if (columnToProperty[i] != -1)  
            {  
                PropertyDescriptor prop = props[columnToProperty[i]];  
                Class<?> propType = prop.getPropertyType();  
  
                Object value = null;  
                if (propType != null)  
                {  
                    value = processColumn(rs, i, propType);  
                    if ((value == null) && (propType.isPrimitive())) {  
                        value = primitiveDefaults.get(propType);  
                    }  
                }  
                callSetter(bean, prop, value);  
            }  
        }  
        return bean;  
    }  
  
    private void callSetter(Object target, PropertyDescriptor prop, Object value)  
            throws SQLException  
    {  
        Method setter = prop.getWriteMethod();  
        if (setter == null) {  
            return;  
        }  
        Class<?>[] params = setter.getParameterTypes();  
        try  
        {  
            if ((value instanceof java.util.Date))  
            {  
                String targetType = params[0].getName();  
                if ("java.sql.Date".equals(targetType))  
                {  
                    value = new java.sql.Date(((java.util.Date)value).getTime());  
                }  
                else if ("java.sql.Time".equals(targetType))  
                {  
                    value = new Time(((java.util.Date)value).getTime());  
                }  
                else if ("java.sql.Timestamp".equals(targetType))  
                {  
                    Timestamp tsValue = (Timestamp)value;  
                    int nanos = tsValue.getNanos();  
                    value = new Timestamp(tsValue.getTime());  
                    ((Timestamp)value).setNanos(nanos);  
                }  
            }  
            else if (((value instanceof String)) && (params[0].isEnum()))  
            {  
            	Class c = params[0].asSubclass(Enum.class);
                value = Enum.valueOf(c, (String)value);  
            }  
            if (isCompatibleType(value, params[0])) {  
                setter.invoke(target, new Object[] { value });  
            } else {  
                throw new SQLException("Cannot set " + prop.getName() + ": incompatible types, cannot convert " + value.getClass().getName() + " to " + params[0].getName());  
            }  
        }  
        catch (IllegalArgumentException e)  
        {  
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());  
        }  
        catch (IllegalAccessException e)  
        {  
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());  
        }  
        catch (InvocationTargetException e)  
        {  
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());  
        }  
    }  
  
    private boolean isCompatibleType(Object value, Class<?> type)  
    {  
        if ((value == null) || (type.isInstance(value))) {  
            return true;  
        }  
        if ((type.equals(Integer.TYPE)) && ((value instanceof Integer))) {  
            return true;  
        }  
        if ((type.equals(Long.TYPE)) && ((value instanceof Long))) {  
            return true;  
        }  
        if ((type.equals(Double.TYPE)) && ((value instanceof Double))) {  
            return true;  
        }  
        if ((type.equals(Float.TYPE)) && ((value instanceof Float))) {  
            return true;  
        }  
        if ((type.equals(Short.TYPE)) && ((value instanceof Short))) {  
            return true;  
        }  
        if ((type.equals(Byte.TYPE)) && ((value instanceof Byte))) {  
            return true;  
        }  
        if ((type.equals(Character.TYPE)) && ((value instanceof Character))) {  
            return true;  
        }  
        if ((type.equals(Boolean.TYPE)) && ((value instanceof Boolean))) {  
            return true;  
        }  
        return false;  
    }  
  
    protected <T> T newInstance(Class<T> c)  
            throws SQLException  
    {  
        try  
        {  
            return c.newInstance();  
        }  
        catch (InstantiationException e)  
        {  
            throw new SQLException("Cannot create " + c.getName() + ": " + e.getMessage());  
        }  
        catch (IllegalAccessException e)  
        {  
            throw new SQLException("Cannot create " + c.getName() + ": " + e.getMessage());  
        }  
    }  
  
    private PropertyDescriptor[] propertyDescriptors(Class<?> c)  
            throws SQLException  
    {  
        BeanInfo beanInfo = null;  
        try  
        {  
            beanInfo = Introspector.getBeanInfo(c);  
        }  
        catch (IntrospectionException e)  
        {  
            throw new SQLException("Bean introspection failed: " + e.getMessage());  
        }  
        return beanInfo.getPropertyDescriptors();  
    }  
  
    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props)  
            throws SQLException  
    {  
        int cols = rsmd.getColumnCount();  
        int[] columnToProperty = new int[cols + 1];  
        Arrays.fill(columnToProperty, -1);  
        for (int col = 1; col <= cols; col++)  
        {  
            String columnName = rsmd.getColumnLabel(col);  
            if ((null == columnName) || (0 == columnName.length())) {  
                columnName = rsmd.getColumnName(col);  
            }  
            String propertyName = (String)this.columnToPropertyOverrides.get(columnName);  
            if (propertyName == null) {  
                propertyName = columnName;  
            }  
            for (int i = 0; i < props.length; i++) {  
                if (propertyName.equalsIgnoreCase(props[i].getName()))  
                {  
                    columnToProperty[col] = i;  
                    break;  
                }  
            }  
        }  
        return columnToProperty;  
    }  
  
    protected Object processColumn(ResultSet rs, int index, Class<?> propType)  
            throws SQLException  
    {  
        if ((!propType.isPrimitive()) && (rs.getObject(index) == null)) {  
            return null;  
        }  
        if (propType.equals(String.class)) {  
            return rs.getString(index);  
        }  
        if ((propType.equals(Integer.TYPE)) || (propType.equals(Integer.class))) {  
            return Integer.valueOf(rs.getInt(index));  
        }  
        if ((propType.equals(Boolean.TYPE)) || (propType.equals(Boolean.class))) {  
            return Boolean.valueOf(rs.getBoolean(index));  
        }  
        if ((propType.equals(Long.TYPE)) || (propType.equals(Long.class))) {  
            return Long.valueOf(rs.getLong(index));  
        }  
        if ((propType.equals(Double.TYPE)) || (propType.equals(Double.class))) {  
            return Double.valueOf(rs.getDouble(index));  
        }  
        if ((propType.equals(Float.TYPE)) || (propType.equals(Float.class))) {  
            return Float.valueOf(rs.getFloat(index));  
        }  
        if ((propType.equals(Short.TYPE)) || (propType.equals(Short.class))) {  
            return Short.valueOf(rs.getShort(index));  
        }  
        if ((propType.equals(Byte.TYPE)) || (propType.equals(Byte.class))) {  
            return Byte.valueOf(rs.getByte(index));  
        }  
        if (propType.equals(Timestamp.class)) {  
            return rs.getTimestamp(index);  
        }  
        if (propType.equals(SQLXML.class)) {  
            return rs.getSQLXML(index);  
        }  
        return rs.getObject(index);  
    }  
}
  1. SqlFactory类主要是通过反射技术,将实体转换为对应的insert,update,delete语句
package com.yunlaiwu.orm;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yunlaiwu.utils.ReflectUtils;

public class SqlFactory {

	private static Logger logger = LoggerFactory.getLogger(SqlFactory.class); 
	
	public static String createInsertSql(Object entity, OrmBridge bridge) {
		StringBuilder sb = new StringBuilder();
		
		sb.append(" INSERT INTO ")
		  .append(bridge.getTableName()).append(" (");
		
		List<String> properties = bridge.listProperties();
		for (int i=0;i<properties.size();i++) {
			String property = properties.get(i);
			String column = property;
			if (bridge.getOverrideProperty(property) != null) {
				column = bridge.getOverrideProperty(property);
			}
			sb.append("`" + column + "`");
			if (i<properties.size()-1) {
				sb.append(",");
			}
		}
		sb.append(") VALUES (");
		
		for (int i=0;i<properties.size();i++) {
			String property = properties.get(i);
			Object value;
			try{
				value = ReflectUtils.getMethodValue(entity, property);
				sb.append("'"+value+"'");
				if (i < properties.size()-1) {
					sb.append(",");
				}
			}catch(Exception e) {
				logger.error("createInsertSql failed",e);
			}
		}
		sb.append(")");
		return sb.toString();
	}
	
	public static String createUpdateSql(Object entity, OrmBridge bridge) {
		StringBuilder sb = new StringBuilder();
		sb.append(" UPDATE ").append(bridge.getTableName())
		  .append(" SET ");
		sb.append(object2SetterSql(entity, bridge));
		sb.append(createWhereClauseSql(entity, bridge));
		
		return sb.toString();
	}
	
	
	private static String object2SetterSql(Object entity, OrmBridge bridge) {
		StringBuilder sb = new StringBuilder();
		Map<String, Method> getterMap = bridge.getGetterMap();
		
		for (Map.Entry<String, Method> entry:getterMap.entrySet()) {
			String property = entry.getKey();
			Object value;
			try{
				value = ReflectUtils.getMethodValue(entity, property);
				if (sb.length() > 0) {
					sb.append(", ");
				}
				String column = entry.getKey();
				if (bridge.getOverrideProperty(property) != null) {
					column = bridge.getOverrideProperty(property);
				}
				sb.append("`"+column+"` = '" +value+"'");
			}catch(Exception e) {
				logger.error("object2SetterSql failed",e);
			}
		}
		return sb.toString();
	}
	
	public static String createDeleteSql(Object entity, OrmBridge bridge) {
		StringBuilder sb = new StringBuilder();
		sb.append(" DELETE FROM ")
		  .append(bridge.getTableName())
		  .append(createWhereClauseSql(entity, bridge));
		  
		return sb.toString();
	}
	
	
	private static String createWhereClauseSql(Object entity, OrmBridge bridge) {
		StringBuilder sb = new StringBuilder();
		sb.append(" WHERE 1=1");  //占位申明,避免拼接sql需要考虑and
		List<String> properties = bridge.getQueryProperties();
		for (int i=0;i<properties.size();i++) {
			String property = properties.get(i);
			Object colValue;
			try{
				colValue = ReflectUtils.getMethodValue(entity, property);
				String column = property;
				if (bridge.getOverrideProperty(property) != null) {
					column = bridge.getOverrideProperty(property);
				}
				sb.append(" AND `" + column + "` = '" + colValue + "'");
			}catch(Exception e) {
				logger.error("createWhereClauseSql failed",e);
			}
		}
		return sb.toString();
	}

}
  1. DbUtils是最重要的工具类了,提供了根据查询语句返回一个实体对象或实体对象列表;提供执行更新、插入、删除操作的接口。更为通用的是,提供通过sql返回一个map对象,map列表的接口。有了这些接口,单表查询、多表查询、执行任意sql都成为可能。
package com.yunlaiwu.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yunlaiwu.orm.BeanProcessor;
import com.yunlaiwu.orm.OrmBridge;
import com.yunlaiwu.orm.OrmProcessor;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DbUtils {

	private static Logger logger = LoggerFactory.getLogger(DbUtils.class); 

	// 生成c3p0数据源对象,会自动读取配置文件
	private static ComboPooledDataSource cpds = new ComboPooledDataSource();

	static{
		try {
			//加载JDBC驱动
			cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" );
			cpds.setJdbcUrl( "jdbc:mysql://localhost/world?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC" );
			cpds.setUser("root");                                  
			cpds.setPassword("123456");  

		}catch(Exception e) {
			logger.error("DbUtils init failed", e);
		}
	}


	/**
	 * 查询返回一个bean实体
	 * @param sql
	 * @param entity
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T queryOne(String sql, Class<?> entity) {
		OrmBridge bridge = OrmProcessor.INSTANCE.getOrmBridge(entity);
		if (bridge == null || entity == null || StringUtils.isEmpty(sql)) {
			return null;
		}
		Connection connection = null;
		Statement statement = null;
		try{
			connection = cpds.getConnection();
			statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);	
			while (resultSet.next()) {
				return  (T) new BeanProcessor(bridge.getColumnToPropertyOverride()).toBean(resultSet, entity);
			}
		}catch(Exception e) {
			logger.error("DbUtils queryOne failed", e);
		}finally {
			if (connection != null) {
				try{
					connection.close();
				}catch(Exception e2) {
					logger.error("DbUtils queryOne failed", e2);
				}
			}
		}
		return null;
	}

	/**
	 * 查询返回bean实体列表
	 * @param sql
	 * @param entity
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<T> queryMany(String sql, Class<?> entity) {
		List<T> result = new ArrayList<>();
		Connection connection = null;
		Statement statement = null;
		try{
			connection = cpds.getConnection();
			statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);	
			Object bean = entity.newInstance();
			while (resultSet.next()) {
				bean = new BeanProcessor().toBean(resultSet, entity);
				result.add((T) bean);
			}
		}catch(Exception e) {
			logger.error("DbUtils queryMany failed", e);
		}finally {
			if (connection != null) {
				try{
					connection.close();
				}catch(Exception e2) {
					logger.error("DbUtils queryMany failed", e2);
				}
			}
		}
		return result;
	}

	/**
	 * 查询返回一个map
	 * @param sql
	 * @param entity
	 * @return
	 */
	public static Map<String, Object> queryMap(String sql) {
		Connection connection = null;
		Statement statement = null;
		Map<String, Object> result = new HashMap<>();
		try{
			connection = cpds.getConnection();
			statement = connection.createStatement();
			ResultSet rs = statement.executeQuery(sql);	
			ResultSetMetaData rsmd = rs.getMetaData();

			while (rs.next()) {
				int cols = rsmd.getColumnCount();
				for (int i = 1; i <= cols; i++)
				{
					String columnName = rsmd.getColumnLabel(i);
					if ((null == columnName) || (0 == columnName.length())) {
						columnName = rsmd.getColumnName(i);
					}
					result.put(columnName, rs.getObject(i));
				}
				break;
			}
		}catch(Exception e) {
			logger.error("DbUtils queryMap failed", e);
		}finally {
			if (connection != null) {
				try{
					connection.close();
				}catch(Exception e2) {
					logger.error("DbUtils queryMap failed", e2);
				}
			}
		}
		return result;
	}

	/**
	 * 查询返回一个map
	 * @param sql
	 * @param entity
	 * @return
	 */
	public static List<Map<String, Object>> queryMapList(String sql) {
		Connection connection = null;
		Statement statement = null;
		List<Map<String, Object>> result = new ArrayList<>();
		try{
			connection = cpds.getConnection();
			statement = connection.createStatement();
			ResultSet rs = statement.executeQuery(sql);	
			ResultSetMetaData rsmd = rs.getMetaData();

			while (rs.next()) {
				int cols = rsmd.getColumnCount();
				Map<String, Object> map = new HashMap<>();
				for (int i = 1; i <= cols; i++)
				{
					String columnName = rsmd.getColumnLabel(i);
					if ((null == columnName) || (0 == columnName.length())) {
						columnName = rsmd.getColumnName(i);
					}
					map.put(columnName, rs.getObject(i));
				}
				result.add(map);
			}
		}catch(Exception e) {
			logger.error("DbUtils queryMapList failed", e);
		}finally {
			if (connection != null) {
				try{
					connection.close();
				}catch(Exception e2) {
					logger.error("DbUtils queryMapList failed", e2);
				}
			}
		}
		return result;
	}

	/**
	 * 执行特定的sql语句
	 * @param sql
	 * @return
	 */
	public static boolean executeSql(String sql) {
		if (StringUtils.isEmpty(sql)) {
			return true;
		}
		Connection connection = null;
		Statement statement = null;
		try{
			connection = cpds.getConnection();
			statement = connection.createStatement();
			statement.execute(sql);	
			return true;
		}catch (Exception e) {
			logger.error("DbUtils executeSql failed", e);
		}finally {
			if (connection != null) {
				try{
					connection.close();
				}catch(Exception e2) {
					logger.error("DbUtils executeSql failed", e2);
				}
			}
		}
		return false;
	}

}
  1. junit测试代码如下
package com.yunlaiwu.test;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

import com.kingston.entity.Player;
import com.kingston.orm.OrmProcessor;
import com.kingston.utils.DbUtils;


public class TestEntity {

	@Before
	public void init() {
		OrmProcessor.INSTANCE.initOrmBridges();
	}

	@Test
	public void testQuery() {
		Player player = DbUtils.queryOne("select * from player where id=1" , Player.class);
		assertTrue(player.getName().equals("kingston"));
	}

	@Test
	public void testUpdate() {
		Player player = DbUtils.queryOne("select * from player where id=1" , Player.class);
		player.setName("Hello");
		player.setUpdate();
		player.save();

		//check
		Player tmpPlayer = DbUtils.queryOne("select * from player where id=1" , Player.class);
		assertTrue(tmpPlayer.getName().equals("Hello"));
		
		//rollback
		player.setName("kingston");
		player.setUpdate();
		player.save();
	}

	@Test
	public void testInsert() {
		Player player = new Player();
		player.setNo(666);
		player.setName("younger");
		player.setInsert();
		
		player.save();
		
		//check
		Player tmpPlayer = DbUtils.queryOne("select * from player where id=" + player.getNo() , Player.class);
		assertTrue(tmpPlayer.getName().equals("younger"));
		
		//rollback
		player.setDelete();
		player.save();
	}

	@Test
	public void testDelete() {
		Player player = DbUtils.queryOne("select * from player where id=1" , Player.class);
		player.setDelete();
		player.save();
		
		//check
		Player tmpPlayer = DbUtils.queryOne("select * from player where id=" + player.getNo() , Player.class);
		assertTrue(tmpPlayer == null);
		
		//rollback
		player.setName("kingston");
		player.setInsert();
		player.save();
	}
}