本人封装的dao层的泛型实现,目前只支持mysql
精华的4个类的代码贴出来了,commons是完整的代码,entityapp是自动代码生成器
这个dao层封装的优点在于:
1.dao层基本不用写任何代码,因为EntityDaoSupport 这个类已经涵盖大多数的数据库操作
2.完整的代码中有个CascadeParamMethodArgumentResolver的类可以用来解决spring mvc中表示层方法参数中复杂对象参数的映射,级联参数格式:a.b.c (表单参数) 或 a[b][c] (ajax请求对象参数),这个类要配置在xml文件中
3.还有一些常用的Converter类,这些也是要配置的,配置后就不需要手工转换了
这个的缺点:
1.底层用了大量的反射和泛型,尚不清楚会不会影响程序的性能,不过我做的项目吞吐量也挺大的,没有发现严重的问题

2.代码写的比较久了,可能存在一些特殊的情况没有考虑到的,也许会有错误,这个就需要大家一起来改进啦

文章来自这个牛人大哥:http://www.oschina.net/code/snippet_1245103_33821


   注意这个架构经过本人改造的。环境是Eclipse4.4.x以上。JDK是1.7,Maven是4.x也就是说Eclipse自带的,MySQL是5.x以上,在学这个架构之前必须懂这些Maven运用。


package com.flong.commons.persistence;import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;import com.flong.commons.persistence.annotation.Column;
import com.flong.commons.persistence.annotation.Id;
import com.flong.commons.persistence.dao.impl.BaseDomain;
import com.flong.commons.persistence.exception.AnnotationNotFoundException;
import com.flong.commons.persistence.exception.ErrorCode;
import com.flong.commons.persistence.exception.FieldColumnMappingException;
import com.flong.commons.utils.ObjectUtil;/*** 实体抽象类** 创建日期:2012-9-24* @author wangk*/
public abstract class Entity extends BaseDomain implements Comparable<Entity> {/** 序列化版本标识 */private static final long serialVersionUID = 4224390531787078169L;/** 列访问操作符 */public static final String COLUMN_ACCESS_OPERATOR = ".";/** 表后缀连接符 */public static final String TABLE_SUFFIX_CONNECTOR = "_";/** 降序排序常量 */public static final String ORDER_DESCENDING = "DESC";/** 关联链(全部的关联类型)常量 */public static final Class<?>[] ASSOCIATION_LINK_ALL = null;/** 注解表名默认值 */public static final String TABLE_NAME_DEFAULT = "";/** 注解列名默认值 */public static final String COLUMN_NAME_DEFAULT = "";/** 注解列类型默认值 */public static final int COLUMN_TYPE_DEFAULT = 0;/** 注解列长度默认值 */public static final int COLUMN_SIZE_DEFAULT = 0;/** 注解外键引用实体类类型默认值 */public static final Class<? extends Entity> REFERENCE_CLASS_DEFAULT = Entity.class;@Overridepublic int hashCode() {final int prime = 31;int result = super.hashCode();String idstr = identityString();Long id = idstr == null ? 0 : Long.valueOf(idstr);result = prime * result + (int) (id ^ (id >>> 32));return result;}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj == null || getClass() != obj.getClass()) {return false;}Entity other = (Entity) obj;String idstr = identityString();String oidstr = other.identityString();if(idstr == oidstr) {return true;}if(idstr == null) {return false;}return idstr.equals(oidstr);}@Overridepublic int compareTo(Entity other) {String idstr = identityString();String oidstr = other.identityString();if(idstr == null && oidstr == null) {return 0;}if(idstr == null) {return 1;}if(oidstr == null) {return -1;}return new BigDecimal(idstr).compareTo(new BigDecimal(oidstr));}/*** 获得实体对象的ID值的字符串形式** @return* 创建日期:2012-9-24* 修改说明:* @author wangk*/public String identityString() {Class<? extends Entity> clazz = getClass();for (Field field : ObjectUtil.getAllField(clazz)) {Id id = field.getAnnotation(Id.class);if(id != null) {try {Object value = new PropertyDescriptor(field.getName(), clazz).getReadMethod().invoke(this);return value == null ? null : value.toString();} catch (Exception e) {throw new RuntimeException(e);}}}throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_ID, "Annotation " + Id.class.getName() + " not found for " + clazz.getName());}/*** 获得当前对象和other对象属性值不同的列集合,当other的类型和当前对象所属类型不同时将抛出RuntimeException** @param other         比较的对象* @return List<String> 属性值不同的列集合      * 创建日期:2012-11-21* 修改说明:* @author wangk*/public List<String> getDifferentColumns(Entity other) {List<String> differentColumns = new ArrayList<String>();Class<? extends Entity> clazz = getClass();for (Field field  : ObjectUtil.getAllField(clazz)) {Column column = field.getAnnotation(Column.class);if(column == null) {continue;}String name = column.value();if(name.equals(COLUMN_NAME_DEFAULT)) {name = field.getName().toUpperCase();}Object value = null;Object otherValue = null;try {Method method = new PropertyDescriptor(field.getName(), clazz).getReadMethod();value = method.invoke(this);otherValue = method.invoke(other);} catch (Exception e) {throw new RuntimeException(e);}if(value==null && otherValue!=null || value!=null && !value.equals(otherValue)) {differentColumns.add(name);}}return differentColumns;}/*** 获得指定列名的属性值** @param <T>         属性类型参数,必须用属性的实际类型及其父类型接收返回值,否则将抛出类型转换异常*                          指定的列为空或不存在时将抛出RuntimeException* @param columnName  列名* @return T          属性值* 创建日期:2012-11-21* 修改说明:* @author wangk*/public <T> T getFieldValue(String columnName) {Class<? extends Entity> clazz = getClass();for (Field field  : ObjectUtil.getAllField(clazz)) {Column column = field.getAnnotation(Column.class);if(column == null) {continue;}String name = column.value();if(name.equals(COLUMN_NAME_DEFAULT)) {name = field.getName().toUpperCase();}if(name.equalsIgnoreCase(columnName)) {try {@SuppressWarnings("unchecked")T value = (T)new PropertyDescriptor(field.getName(), clazz).getReadMethod().invoke(this);return value;} catch (Exception e) {throw new RuntimeException(e);}}}throw new FieldColumnMappingException(ErrorCode.FIELD_COLUMN_MAPPING_AS_NOT_EXIST_COLUMN, "Column name " + columnName + " not exist for " + clazz.getName());}/*** 判断实体对象的状态是否为瞬时状态(ID值为空)** @return* 创建日期:2012-9-24* 修改说明:* @author wangk*/public boolean isTransient() {return identityString() == null;}}
package com.flong.commons.persistence.dao.impl;import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;import com.flong.commons.lang.exception.DaoAccessException;
import com.flong.commons.persistence.Entity;
import com.flong.commons.persistence.annotation.Association;
import com.flong.commons.persistence.annotation.Reference;
import com.flong.commons.persistence.bean.DataStore;
import com.flong.commons.persistence.bean.PagingParameter;
import com.flong.commons.persistence.builder.SimpleSqlBuilder;
import com.flong.commons.persistence.condition.Condition;
import com.flong.commons.persistence.dao.EntityDao;
import com.flong.commons.persistence.exception.AnnotationNotFoundException;
import com.flong.commons.persistence.exception.DuplicateRecordException;
import com.flong.commons.persistence.exception.ErrorCode;
import com.flong.commons.persistence.exception.FieldColumnMappingException;
import com.flong.commons.persistence.exception.IllegalRecordException;
import com.flong.commons.utils.ObjectUtil;/*** 实体Dao支持类** 创建日期:2012-9-24* @author wangk*/
@SuppressWarnings("all")
public abstract class EntityDaoSupport<E extends Entity> extends BaseDaoSupport implements EntityDao<E> {/** 日志对象 */private static final Logger logger = Logger.getLogger(BaseDaoSupport.class);/** 条件关键字集合 */private static final List<String> CONDITION_KEYWORDS = new ArrayList<String>();/** 条件运算符(正则表达式形式)集合 */private static final List<String> CONDITION_OPERATORS = new ArrayList<String>();static {/** 初始化条件关键字集合 */CONDITION_KEYWORDS.add("IS");CONDITION_KEYWORDS.add("NOT");CONDITION_KEYWORDS.add("NULL");CONDITION_KEYWORDS.add("LIKE");CONDITION_KEYWORDS.add("BETWEEN");CONDITION_KEYWORDS.add("AND");CONDITION_KEYWORDS.add("OR");CONDITION_KEYWORDS.add("IN");CONDITION_KEYWORDS.add("ASC");CONDITION_KEYWORDS.add("DESC");/** 初始化条件运算符集合(= < > + - * / % ( ) , ?),组合符号(<> <= >=)需要特殊处理 */CONDITION_OPERATORS.add("=");CONDITION_OPERATORS.add("<");CONDITION_OPERATORS.add(">");CONDITION_OPERATORS.add("\\+");CONDITION_OPERATORS.add("\\-");CONDITION_OPERATORS.add("\\*");CONDITION_OPERATORS.add("/");CONDITION_OPERATORS.add("%");CONDITION_OPERATORS.add("\\(");CONDITION_OPERATORS.add("\\)");CONDITION_OPERATORS.add("\\,");CONDITION_OPERATORS.add("\\?");}/** SQL语句构建对象 */protected SimpleSqlBuilder<E> simpleSqlBuilder;/** 实体类类型 */private Class<E> entityClass;/*** 构造方法  通过反射初始化entityClass* 创建日期:2012-10-8* 修改说明:* @author wangk*/public EntityDaoSupport() {//获得EntityDaoSupport包含泛型实参的类型ParameterizedType parameterizedType = (ParameterizedType)getClass().getGenericSuperclass();Type[] params = parameterizedType.getActualTypeArguments();Class<E> entityClass = (Class<E>)params[0];init(entityClass);}/*** 构造方法* @param entityClass 实体类类型* 创建日期:2012-9-25* 修改说明:* @author wangk*/public EntityDaoSupport(Class<E> entityClass) {init(entityClass);}/*** 初始化方法** @param entityClass  实体类类型* 创建日期:2012-10-11* 修改说明:* @author wangk*/private void init(Class<E> entityClass) {this.entityClass = entityClass;//初始化simpleSqlBuildersimpleSqlBuilder = new SimpleSqlBuilder<E>(entityClass);//检查实体所有的引用属性列是否存在和引用属性类型和被引用属性类型是否相同simpleSqlBuilder.checkReferenceFields();//检查关联属性是否存在非自身循环关联并记录debug级别日志for (Class<?> association : simpleSqlBuilder.getAssociationLink()) {logger.debug(association.getName());}}/*** 获得SQL语句构建对象** @return* 创建日期:2012-9-25* 修改说明:* @author wangk*/public SimpleSqlBuilder<E> getSimpleSqlBuilder() {return simpleSqlBuilder;}@Overridepublic <K extends Number> E get(K id, Class<?>... associationLink) throws DaoAccessException {if(associationLink != null && associationLink.length == 0) {try {return jdbcTemplate.queryForObject(simpleSqlBuilder.getQuerySimpleSql(), simpleSqlBuilder.getRowMapper(), id);} catch (EmptyResultDataAccessException e) {//只处理空结果异常,表示没有对应的记录,返回nulllogger.warn(e);return null;} catch (Exception e) {throw new DaoAccessException(e);}}return get(Condition.eq(simpleSqlBuilder.getFieldColumnMapping().get(simpleSqlBuilder.getIdField()), id), associationLink);}@Overridepublic E get(String sql, Object... params) throws DaoAccessException {return getUniqueEntity(query(sql, params));}@Overridepublic E get(String sql, List<Object> params) throws DaoAccessException {return get(sql, params.toArray());}@Overridepublic E get(String sql, Map<String, Object> params) throws DaoAccessException {return getUniqueEntity(query(sql, params));}@Overridepublic E get(Condition condition, Class<?>... associationLink) throws DaoAccessException {return getUniqueEntity(query(condition, associationLink));}@Overridepublic <K extends Number> K save(E entity) throws DaoAccessException {return saveWithId(entity, null);}@Overridepublic <K extends Number> void save(E entity, K id) throws DaoAccessException {saveWithId(entity, id);}@Overridepublic void update(E entity) throws DaoAccessException {try {if(entity.isTransient()) {throw new IllegalRecordException(ErrorCode.ILLEGAL_RECORD_AS_UPDATE_TRANSIENT,"The record " + entity + " is transient!");}Map<String, Object> params = simpleSqlBuilder.getSqlParameters(entity);namedParameterJdbcTemplate.update(simpleSqlBuilder.getUpdateSql(params.keySet()), params);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic void saveOrUpdate(E entity) throws DaoAccessException {if(entity.isTransient()) {save(entity);} else {update(entity);}}@Overridepublic <K extends Number> void delete(K id) throws DaoAccessException {try {jdbcTemplate.update(simpleSqlBuilder.getDeleteSql(), id);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic List<E> query(String sql, Object... params) throws DaoAccessException {return query(sql, null, params).getDatas();}@Overridepublic List<E> query(String sql, List<Object> params) throws DaoAccessException {return query(sql, params.toArray());}@Overridepublic List<E> query(String sql, Map<String, Object> params) throws DaoAccessException {return query(sql, null, params).getDatas();}@Overridepublic List<E> query(Condition condition, Class<?>... associationLink) throws DaoAccessException {String orders = null;return query(condition, orders, associationLink);}@Overridepublic List<E> query(Condition condition, String orders, Class<?>... associationLink) throws DaoAccessException {return query(condition, orders, null, associationLink).getDatas();}@Overridepublic DataStore<E> query(String sql, PagingParameter paging, Object... params) throws DaoAccessException {sql = handleSimpleSql(sql);return queryDataStore(sql, params, getRowMapperBySql(sql), paging);}@Overridepublic DataStore<E> query(String sql, PagingParameter paging,List<Object> params) throws DaoAccessException {return query(sql, paging, params.toArray());}@Overridepublic DataStore<E> query(String sql, PagingParameter paging,Map<String, Object> params) throws DaoAccessException {sql = handleSimpleSql(sql);return queryDataStore(sql, params, getRowMapperBySql(sql), paging);}@Overridepublic DataStore<E> query(Condition condition, PagingParameter paging, Class<?>... associationLink) throws DaoAccessException {return query(condition, null, paging, associationLink);}@Overridepublic DataStore<E> query(Condition condition, String orders, PagingParameter paging, Class<?>... associationLink) throws DaoAccessException {Object[] params = new Object[0];if(condition != null) {params = condition.getParameters();}if(associationLink != null && associationLink.length == 0) {String sql = condition==null?"":condition.toSqlString();if(orders == null) {orders = simpleSqlBuilder.getFieldColumnMapping().get(simpleSqlBuilder.getIdField());}if(!sql.equals("")) {sql += " ";}sql += "ORDER BY " + orders;return query(sql, paging, params);}return queryDataStore(buildAssociationSql(condition, orders, associationLink), params, simpleSqlBuilder.getRowMapper(associationLink), paging);}@Overridepublic List<E> queryAll(Class<?>... associationLink) throws DaoAccessException {String orders = null;return queryAll(orders, associationLink);}@Overridepublic List<E> queryAll(String orders, Class<?>... associationLink) throws DaoAccessException {return query(null, orders, associationLink);}@Overridepublic DataStore<E> queryAll(PagingParameter paging, Class<?>... associationLink) throws DaoAccessException {return queryAll(null, paging, associationLink);}@Overridepublic DataStore<E> queryAll(String orders, PagingParameter paging, Class<?>... associationLink) throws DaoAccessException {return query(null, orders, paging, associationLink);}@Overridepublic int count() throws DaoAccessException {return count(null);}@Overridepublic int count(Condition condition) throws DaoAccessException {try {String sql = simpleSqlBuilder.getQueryCountSql();if(condition == null) {return jdbcTemplate.queryForInt(sql);}sql += " WHERE " + condition.toSqlString();logger.debug(sql);return jdbcTemplate.queryForInt(sql, condition.getParameters());} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic <K extends Number> void deletes(List<K> ids) throws DaoAccessException {if(CollectionUtils.isEmpty(ids)) {return;}try {List<Object[]> batchArgs = new ArrayList<Object[]>();for (K id : ids) {batchArgs.add(new Object[]{id});}jdbcTemplate.batchUpdate(simpleSqlBuilder.getDeleteSql(), batchArgs);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic <K extends Number> void saves(List<E> entitys, K... ids) throws DaoAccessException {if(CollectionUtils.isEmpty(entitys)) {return;}try {int entityCount = entitys.size();int idCount = ids.length;if(idCount > entityCount) {idCount = entityCount;}List<E> withIdEntitys = entitys.subList(0, idCount);List<E> withoutIdEntitys = entitys.subList(idCount, entityCount);if(CollectionUtils.isNotEmpty(withIdEntitys)) {Map<String, Object>[] batchArgs = new Map[idCount];for (int i = 0; i < idCount; i++) {E entity = withIdEntitys.get(i);if(!entity.isTransient()) {throw new DuplicateRecordException(ErrorCode.DUPLICATE_RECORDE_AS_SAVE_ENTITY, "The record that whoes id equals " + entity.identityString() + " is already exist!");}batchArgs[i] = simpleSqlBuilder.getAllSqlParameter(entity);batchArgs[i].put(simpleSqlBuilder.getIdField(), ids[i]);}namedParameterJdbcTemplate.batchUpdate(simpleSqlBuilder.getIncludeIdFieldInsertSql(), batchArgs);}if(CollectionUtils.isNotEmpty(withoutIdEntitys)) {Map<String, Object>[] batchArgs = new Map[withoutIdEntitys.size()];for (int i = 0; i < batchArgs.length; i++) {E entity = withoutIdEntitys.get(i);if(!entity.isTransient()) {throw new DuplicateRecordException(ErrorCode.DUPLICATE_RECORDE_AS_SAVE_ENTITY, "The record that whoes id equals " + entity.identityString() + " is already exist!");}batchArgs[i] = simpleSqlBuilder.getAllSqlParameter(entity);}namedParameterJdbcTemplate.batchUpdate(simpleSqlBuilder.getInsertSql(), batchArgs);}} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic void updates(List<E> entitys) throws DaoAccessException {if(CollectionUtils.isEmpty(entitys)) {return;}try {Map<String, Object>[] batchArgs = new Map[entitys.size()];for (int i = 0; i < batchArgs.length; i++) {E entity = entitys.get(i);if(entity.isTransient()) {throw new IllegalRecordException(ErrorCode.ILLEGAL_RECORD_AS_UPDATE_TRANSIENT,"The record " + entity + " is transient!");}batchArgs[i] = simpleSqlBuilder.getAllSqlParameter(entity);}namedParameterJdbcTemplate.batchUpdate(simpleSqlBuilder.getUpdateSql(), batchArgs);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic void saveOrUpdates(List<E> entitys) throws DaoAccessException {if(CollectionUtils.isEmpty(entitys)) {return;}List<E> saves = new ArrayList<E>();List<E> updates = new ArrayList<E>();for (E entity : entitys) {if(entity.isTransient()) {saves.add(entity);} else {updates.add(entity);}}saves(saves);updates(updates);}@Overridepublic E getReferenced(E entity) throws DaoAccessException {return getReferenced(entity, entityClass);}@Overridepublic E getReferenced(Object referenceValue) throws DaoAccessException {return getReferenced(referenceValue, entityClass);}@Overridepublic <R extends Entity> R getReferenced(E entity, Class<R> referencedClass) throws DaoAccessException {Object value = null;try {String field = simpleSqlBuilder.getReferenceField(referencedClass);if(field == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referencedClass.getName() + " of " + entityClass.getName());}value = new PropertyDescriptor(field, entityClass).getReadMethod().invoke(entity);} catch (Exception e) {throw new DaoAccessException(e);}if(value == null) {return null;}return getReferenced(value, referencedClass);}@Overridepublic <R extends Entity> R getReferenced(Object referenceValue,Class<R> referencedClass) throws DaoAccessException {try {String referencedColumn = simpleSqlBuilder.getReferencedColumn(referencedClass);if(referencedColumn == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referencedClass.getName() + " of " + entityClass.getName());}SimpleSqlBuilder<R> referencedSqlBuilder = new SimpleSqlBuilder<R>(referencedClass);String sql = referencedSqlBuilder.getQueryAllSql() + " WHERE " + referencedColumn + " = ?";logger.debug(sql);return jdbcTemplate.queryForObject(sql, referencedSqlBuilder.getRowMapper(), referenceValue);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic <R extends Entity> List<E> queryReferences(R referenced) throws DaoAccessException {PagingParameter paging = null;return queryReferences(referenced, paging).getDatas();}@Overridepublic <R extends Entity> List<E> queryReferences(Class<R> referencedClass, Object referencedValue) throws DaoAccessException {PagingParameter paging = null;return queryReferences(referencedClass, referencedValue, paging).getDatas();}@Overridepublic <R extends Entity, S extends Entity> List<S> queryReferences(R referenced, Class<S> referenceClass) throws DaoAccessException {return queryReferences(referenced, referenceClass, null).getDatas();}@Overridepublic <R extends Entity, S extends Entity> List<S> queryReferences(Class<R> referencedClass, Object referencedValue,Class<S> referenceClass) throws DaoAccessException {return queryReferences(referencedClass, referencedValue, referenceClass, null).getDatas();}@Overridepublic <R extends Entity> DataStore<E> queryReferences(R referenced,PagingParameter paging) throws DaoAccessException {Object value = getReferencedValue(referenced);if(value == null) {return null;}return queryReferences(referenced.getClass(), value, paging);}@Overridepublic <R extends Entity> DataStore<E> queryReferences(Class<R> referencedClass, Object referencedValue,PagingParameter paging) throws DaoAccessException {String sql = null;try {String field = simpleSqlBuilder.getReferenceField(referencedClass);if(field == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referencedClass.getName() + " of " + entityClass.getName());}sql = simpleSqlBuilder.getQueryAllSql() + " WHERE " + simpleSqlBuilder.getFieldColumnMapping().get(field) + " = ? ORDER BY " +simpleSqlBuilder.getFieldColumnMapping().get(simpleSqlBuilder.getIdField());} catch (Exception e) {throw new DaoAccessException(e);}return queryDataStore(sql, new Object[]{referencedValue}, simpleSqlBuilder.getRowMapper(), paging);}@Overridepublic <R extends Entity, S extends Entity> DataStore<S> queryReferences(R referenced, Class<S> referenceClass, PagingParameter paging) throws DaoAccessException {Object value = getReferencedValue(referenced);if(value == null) {return null;}return queryReferences(referenced.getClass(), value, referenceClass, paging);}@Overridepublic <R extends Entity, S extends Entity> DataStore<S> queryReferences(Class<R> referencedClass, Object referencedValue,Class<S> referenceClass, PagingParameter paging) throws DaoAccessException {String sql = null;SimpleSqlBuilder<S> referenceSqlBuilder = null;try {String referencedColumn = simpleSqlBuilder.getReferencedColumn(referenceClass);if(referencedColumn == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referenceClass.getName() + " of " + entityClass.getName());}String field = simpleSqlBuilder.getReferenceField(referencedClass);if(field == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referencedClass.getName() + " of " + entityClass.getName());}referenceSqlBuilder = new SimpleSqlBuilder<S>(referenceClass);sql = referenceSqlBuilder.getQueryAllSql() + " WHERE " + referencedColumn + " IN (SELECT " + simpleSqlBuilder.getFieldColumnMapping().get(simpleSqlBuilder.getReferenceField(referenceClass)) + " FROM " + simpleSqlBuilder.getTableName() + " WHERE " + simpleSqlBuilder.getFieldColumnMapping().get(field) + " = ?) ORDER BY " + referenceSqlBuilder.getFieldColumnMapping().get(referenceSqlBuilder.getIdField());} catch (Exception e) {throw new DaoAccessException(e);}return queryDataStore(sql, new Object[]{referencedValue}, referenceSqlBuilder.getRowMapper(), paging);}/*** 根据被引用对象获得外键属性值** @param <R>* @param referenced* @return* 创建日期:2012-10-8* 修改说明:* @author wangk*/private <R extends Entity> Object getReferencedValue(R referenced) throws DaoAccessException {try {Class<R> referencedClass = (Class<R>)referenced.getClass();String referencedColumn = simpleSqlBuilder.getReferencedColumn(referencedClass);if(referencedColumn == null) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_REFERENCE, "Annotation " + Reference.class.getName() + " not found for referenced " +referencedClass.getName() + " of " + entityClass.getName());}SimpleSqlBuilder<R> referencedSqlBuilder = new SimpleSqlBuilder<R>(referencedClass);String columnField = referencedSqlBuilder.getColumnField(referencedColumn);if(columnField == null) {throw new FieldColumnMappingException(ErrorCode.FIELD_COLUMN_MAPPING_AS_NOT_EXIST_COLUMN, "ColumnName " + referencedColumn + " not exist for " + referencedClass.getName());}return new PropertyDescriptor(columnField, referencedClass).getReadMethod().invoke(referenced);} catch (Exception e) {throw new DaoAccessException(e);}}/*** 构建关联查询SQL语句** @param condition       查询条件* @param orders          排序对象* @param associationLink 关联链* @return String         关联查询SQL语句* 创建日期:2012-12-5* 修改说明:* @author wangk*/@SuppressWarnings({ "rawtypes", "unchecked" })private String buildAssociationSql(Condition condition, String orders, Class<?>[] associationLink) throws DaoAccessException {try {if(associationLink == null) {associationLink = simpleSqlBuilder.getAssociationLink();}StringBuilder sb1 = new StringBuilder();StringBuilder sb2 = new StringBuilder();String tableName = simpleSqlBuilder.getTableName() + "_0";Map<String, String> fieldColumnMapping = simpleSqlBuilder.getFieldColumnMapping();for (String field : fieldColumnMapping.keySet()) {sb1.append(tableName +  "." + fieldColumnMapping.get(field) + " AS " + tableName +  "_" + fieldColumnMapping.get(field) + ", ");}sb2.append(simpleSqlBuilder.getTableName()).append(" AS ").append(tableName);//关联类型三标志(被关联类型下标、被关联类型、关联类型)集合List<List<Object>> associationFlags = new ArrayList<List<Object>>();for (int i = 0;i < associationLink.length;i++) {Class<?> tempClass = associationLink[i];if(!ObjectUtil.isExtends(tempClass, Entity.class)) {throw new FieldColumnMappingException(ErrorCode.FIELD_COLUMN_MAPPING_AS_ASSOCIATION_TYPE_ERROR,"Association " + tempClass.getName() + " type error for " +entityClass.getName() + ", the type must extends Entity!");}Class<? extends Entity> associationClass = (Class<? extends Entity>)tempClass;SimpleSqlBuilder<? extends Entity> associationSqlBuilder = new SimpleSqlBuilder(associationClass);Class<? extends Entity> clazz = null;SimpleSqlBuilder<? extends Entity> sqlBuilder = null;int j = -1;for (; j < i; j++) {if(j >= 0) {clazz = (Class<? extends Entity>)associationLink[j];} else {clazz = entityClass;}sqlBuilder = new SimpleSqlBuilder(clazz);if(sqlBuilder.getAssociationField(associationClass) != null) {List<Object> associationFlag = new ArrayList<Object>();associationFlag.add(j + 1);associationFlag.add(clazz);associationFlag.add(associationClass);if(!associationFlags.contains(associationFlag)) {//检查指定的关联类型对应的引用属性是否存在sqlBuilder.checkExistReferenceField(associationClass);associationFlags.add(associationFlag);break;}}}if(j == i) {throw new AnnotationNotFoundException(ErrorCode.ANNOTATION_NOT_FOUND_AS_ASSOCIATION,"Annotation " + Association.class.getName() + " not found for association " +associationClass.getName() + " of " + entityClass.getName());}String tableName2 = associationSqlBuilder.getTableName() + "_" + (i + 1);Map<String, String> fieldColumnMapping2 = associationSqlBuilder.getFieldColumnMapping();for (String field : fieldColumnMapping2.keySet()) {sb1.append(tableName2 +  "." + fieldColumnMapping2.get(field) + " AS " + tableName2 +  "_" + fieldColumnMapping2.get(field) + ", ");}String tableName1 = sqlBuilder.getTableName() + "_" + (j + 1);Map<String, String> fieldColumnMapping1 = sqlBuilder.getFieldColumnMapping();sb2.append(" LEFT JOIN " + associationSqlBuilder.getTableName() + " AS " + tableName2 + " ON " + tableName1 + "." + fieldColumnMapping1.get(sqlBuilder.getReferenceField(associationClass)) + " = " + tableName2 + "." + sqlBuilder.getReferencedColumn(associationClass));}sb1.delete(sb1.length() - 2, sb1.length());String sql = "SELECT " + sb1 + " FROM " + sb2;if(condition != null) {sql += " WHERE " + handleSqlColumnPrefix(condition.toSqlString(), associationLink);}if(orders == null) {orders = simpleSqlBuilder.getFieldColumnMapping().get(simpleSqlBuilder.getIdField());}sql += " ORDER BY " + handleSqlColumnPrefix(orders, associationLink);logger.debug(sql);return sql;} catch (Exception e) {throw new DaoAccessException(e);}}/*** 处理SQL子句的列名前缀,以区分来自不同的表*    1. 没有前缀则加上:entityClass#tableName_0.*  2. 有前缀但没有下标则加上下标,如entityClass#tableName.处理成entityClass#tableName_0.*     (关联链中有两个相同的类型时如果没有下标则加上第一次出现的下标,要指定后面类型的列必须带下标)*  3. entityClass下标为0,associationLink的第一个元素下标为1,依次类批(即数组下标加一)*  4. 出现的表名不能以'_数字'结尾** @param sql              条件SQL语句* @param associationLink  关联链* @return* 创建日期:2012-12-5* 修改说明:* @author wangk*/private String handleSqlColumnPrefix(String sql, Class<?>[] associationLink) {if(sql == null) {return null;}StringBuilder sb = new StringBuilder();int start = 0;int end = 0;for (int i = 0; i < sql.length(); i++) {char ch = sql.charAt(i);if(ch == '\'') {end = i;sb.append(handleSubSqlColumnPrefix(sql.substring(start, end), associationLink));start = end;for (i++; i < sql.length(); i++) {if(sql.charAt(i) == ch) {end = i + 1;sb.append(sql.substring(start, end));start = end;break;}}} else if(ch == '(' && sql.substring(i+1).trim().toUpperCase().startsWith("SELECT")) {end = i;sb.append(handleSubSqlColumnPrefix(sql.substring(start, end), associationLink));start = end;Stack<Character> stack = new Stack<Character>();stack.push(ch);for (i++; i < sql.length(); i++) {char ch2 = sql.charAt(i);if(ch2 == ch) {stack.push(ch);} else if(ch2 == ')') {stack.pop();}if(stack.isEmpty()) {end = i + 1;sb.append(sql.substring(start, end));start = end;break;}}}}end = sql.length();sb.append(handleSubSqlColumnPrefix(sql.substring(start, end), associationLink));return sb.toString().trim();}/*** 处理一段SQL子句的列名前缀,不包含子查询和字符串数据** @param subSql          一段SQL子句* @param associationLink 关联链* @return String         处理结果,结尾带空格* 创建日期:2012-12-6* 修改说明:* @author wangk*/private String handleSubSqlColumnPrefix(String subSql, Class<?>[] associationLink) {//处理组合符号(= -> #, < -> @, > -> &)subSql = subSql.replaceAll("\\s*<>\\s*", " @& ").replaceAll("\\s*<=\\s*", " @# ").replaceAll("\\s*>=\\s*", " &# ");for (String operator : CONDITION_OPERATORS) {//处理单个符号subSql = subSql.replaceAll("\\s*" + operator + "\\s*", " " + operator + " ");}subSql = subSql.replaceAll("@&", "<>").replaceAll("@#", "<=").replaceAll("&#", ">=");String[] strings = subSql.split("\\s+");StringBuilder sb = new StringBuilder();for (String string : strings) {if(string.matches("^[A-Za-z_][\\w]*(\\.\\w+)?$") && !CONDITION_KEYWORDS.contains(string.toUpperCase())) {sb.append(handleColumnPrefix(string, associationLink));} else {sb.append(string);}sb.append(" ");}return sb.toString();}/*** 处理列名前缀** @param column           列名* @param associationLink  关联链* @return String          带前缀的列名* 创建日期:2012-12-5* 修改说明:* @author wangk*/@SuppressWarnings({ "rawtypes", "unchecked" })private String handleColumnPrefix(String column, Class<?>[] associationLink) {if(column.indexOf(".") < 0) {return simpleSqlBuilder.getTableName() + "_0." + column;}String[] strings = column.split("\\.");String tableName = strings[0];if(!tableName.matches("^.*_\\d+$")) {int index = 0;if(!tableName.equalsIgnoreCase(simpleSqlBuilder.getTableName())) {int i = 0;for (; i < associationLink.length; i++) {if(tableName.equalsIgnoreCase(new SimpleSqlBuilder(associationLink[i]).getTableName())) {break;}}index = i + 1;}return tableName + "_" + index + "." + strings[1];}return column;}/*** 获得指定entitys的唯一实体对象,没有返回null,两个及以上抛出DuplicateRecordException** @param entitys    实体集合* @return* 创建日期:2012-12-6* 修改说明:* @author wangk*/private E getUniqueEntity(List<E> entitys) {if(CollectionUtils.isEmpty(entitys)) {return null;}if(entitys.size() == 1) {return entitys.get(0);}throw new DuplicateRecordException(ErrorCode.DUPLICATE_RECORDE_AS_GET_ENTITY);}/*** 保存实体对象,ID值由参数指定,如果ID指定为null则由数据库生成** @param <K>     ID类型参数* @param entity  实体对象* @param id      ID值* @return* 创建日期:2012-12-3* 修改说明:* @author wangk*/private <K extends Number> K saveWithId(E entity, K id) throws DaoAccessException {try {if(!entity.isTransient()) {throw new DuplicateRecordException(ErrorCode.DUPLICATE_RECORDE_AS_SAVE_ENTITY,"The record " + entity + " is exist!");}Class<K> idFieldType = (Class<K>)new PropertyDescriptor(simpleSqlBuilder.getIdField(), entityClass).getPropertyType();Map<String, Object> params = simpleSqlBuilder.getSqlParameters(entity);if(id == null) {String sql = simpleSqlBuilder.getInsertSql(params.keySet());KeyHolder keyHolder = new GeneratedKeyHolder();namedParameterJdbcTemplate.update(sql, new MapSqlParameterSource(params), keyHolder);id = idFieldType.getConstructor(String.class).newInstance(keyHolder.getKey().toString());} else {params.put(simpleSqlBuilder.getIdField(), id);String sql = simpleSqlBuilder.getIncludeIdFieldInsertSql(params.keySet());namedParameterJdbcTemplate.update(sql, params);}//设置entity的ID值new PropertyDescriptor(simpleSqlBuilder.getIdField(), entityClass).getWriteMethod().invoke(entity, id);return id;} catch (Exception e) {throw new DaoAccessException(e);}}/*** 处理单表查询的SQL语句,SQL为null或空字符串或只指定WHERE子句时处理成查询全部记录** @param sql       SQL语句* @return String   处理后的SQL语句* 创建日期:2012-12-6* 修改说明:* @author wangk*/private String handleSimpleSql(String sql) {if(StringUtils.isBlank(sql)) {sql = simpleSqlBuilder.getQueryAllSql();} else if(!sql.trim().toUpperCase().startsWith("SELECT")) {if(!sql.trim().toUpperCase().startsWith("WHERE") && !sql.trim().toUpperCase().startsWith("ORDER")) {sql = "WHERE " + sql;}sql = simpleSqlBuilder.getQueryAllSql() + " " + sql;}logger.debug(sql);return sql;}/*** 根据SQL语句获取不同的RowMapper,如果指定了投影列则只映射指定列对应的属性,其他属性为null,否则映射全部属性** @param sql           SQL语句* @return RowMapper<E> 记录映射对象* 创建日期:2012-12-6* 修改说明:* @author wangk*/private RowMapper<E> getRowMapperBySql(String sql) {sql = sql.trim().toUpperCase();String projection = sql.substring(sql.indexOf("SELECT")+6, sql.indexOf("FROM")).trim();if(projection.indexOf("*") >=0) {return simpleSqlBuilder.getRowMapper();}Set<String> columns = new HashSet<String>();for (String column : projection.split(",")) {columns.add(column.trim().replaceAll("\\s.*$", ""));}return simpleSqlBuilder.getRowMapper(columns);}/*** 查询分页数据,如果指定的paging为null或参数不正确则查询出全部的数据** @param <T>           SQL参数类型,限制只能使用Object[]或Map<String, Object>* @param sql           SQL语句* @param params        SQL参数* @param rowMapper     记录映射对象* @param paging        分页参数* @return DataStore<E> 分页数据* @throws DaoAccessException DAO访问异常* 创建日期:2012-12-6* 修改说明:* @author wangk*/private <P, S extends Entity> DataStore<S> queryDataStore(String sql, P params, RowMapper<S> rowMapper, PagingParameter paging) throws DaoAccessException {try {if(paging == null || paging.isInvalid()) {if(params instanceof Map) {return new DataStore<S>(paging, namedParameterJdbcTemplate.query(sql, new MapSqlParameterSource((Map<String, Object>)params), rowMapper));}return new DataStore<S>(paging, jdbcTemplate.query(sql, rowMapper, (Object[])params));}int records = 0;if(params instanceof Map) {records = namedParameterJdbcTemplate.queryForInt(pagingSqlBuilder.getCountSql(sql), (Map<String, Object>)params);} else {records = jdbcTemplate.queryForInt(pagingSqlBuilder.getCountSql(sql), (Object[])params);}if(records < 0) {return null;}if(records == 0) {return new DataStore<S>(records, new ArrayList<S>());}if(params instanceof Map) {return new DataStore<S>(records, namedParameterJdbcTemplate.query(pagingSqlBuilder.getPagingSql(sql, paging), new MapSqlParameterSource((Map<String, Object>)params), rowMapper));}return new DataStore<S>(records, jdbcTemplate.query(pagingSqlBuilder.getPagingSql(sql, paging), rowMapper, (Object[])params));} catch (Exception e) {throw new DaoAccessException(e);}}}
package com.flong.commons.persistence.dao;import java.util.List;
import java.util.Map;import com.flong.commons.lang.exception.DaoAccessException;
import com.flong.commons.persistence.bean.DataStore;
import com.flong.commons.persistence.bean.PagingParameter;
import com.flong.commons.persistence.condition.Condition;
import com.flong.commons.persistence.interfaces.MapRowMapper;/*** 数据查询DAO接口** 创建日期:2012-9-26* @author wangk*/
public interface BaseDao {/*** 查询SQL语句** @param sql                        SQL语句* @param params                     SQL参数* @return List<Map<String, Object>> 查询结果* 创建日期:2012-9-26* 修改说明:* @author wangk*/public List<Map<String, Object>> search(String sql, Object... params) throws DaoAccessException;public List<Map<String, Object>> search(String sql, List<Object> params) throws DaoAccessException;public List<Map<String, Object>> search(String sql, Map<String, Object> params) throws DaoAccessException;/*** 查询SQL语句** @param <R>           行记录类型* @param sql           SQL语句* @param mapRowMapper  Map行数据映射对象* @param params        SQL参数 * @return List<R>      查询结果* 创建日期:2012-9-26* 修改说明:* @author wangk*/public <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper, Object... params) throws DaoAccessException;public <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper, List<Object> params) throws DaoAccessException;public <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper, Map<String, Object> params) throws DaoAccessException;/*** 分页查询数据** @param sql                             SQL语句* @param paging                          分页参数* @param params                          SQL参数* @return DataStore<Map<String, Object>> 分页数据* 创建日期:2012-9-26* 修改说明:* @author wangk*/public DataStore<Map<String, Object>> search(String sql, PagingParameter paging, Object... params) throws DaoAccessException;public DataStore<Map<String, Object>> search(String sql, PagingParameter paging, List<Object> params) throws DaoAccessException;public DataStore<Map<String, Object>> search(String sql, PagingParameter paging, Map<String, Object> params) throws DaoAccessException;/*** 分页查询数据** @param <R>           行记录类型* @param sql           SQL语句* @param mapRowMapper  Map行数据映射对象* @param paging        分页参数* @param params        SQL参数* @return DataStore<R> 分页数据* 创建日期:2012-9-26* 修改说明:* @author wangk*/public <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper, PagingParameter paging, Object... params) throws DaoAccessException;public <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper, PagingParameter paging, List<Object> params) throws DaoAccessException;public <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper, PagingParameter paging, Map<String, Object> params) throws DaoAccessException;/*** 连接查询** @param condition                  查询条件,指定列格式:表名.列名* @param classLink                  连接实体类类型链* @return List<Map<String, Object>> 查询结果,Map.key: 对象名(Class指定的类名的首字母小写形式).属性名* 创建日期:2012-10-19* 修改说明:* @author wangk*/public List<Map<String, Object>> join(Condition condition, Class<?>... classLink) throws DaoAccessException;/*** 连接查询** @param condition 查询条件* @param orders    排序对象* @param classLink 连接实体类类型链* @return          查询结果* 创建日期:2012-10-19* 修改说明:* @author wangk*/public List<Map<String, Object>> join(Condition condition, String orders, Class<?>... classLink) throws DaoAccessException;/*** 连接查询(内连接)** @param <R>          映射类型参数* @param condition    查询条件* @param mapRowMapper 行匹配对象* @param classLink    连接实体类型链(从子表到父表的顺序)* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public <R> List<R> join(Condition condition, MapRowMapper<R> mapRowMapper, Class<?>... classLink) throws DaoAccessException;    /*** 连接查询** @param <R>          映射类型参数* @param condition    查询条件* @param orders       排序对象* @param mapRowMapper 行匹配对象* @param classLink    连接实体类型链* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public <R> List<R> join(Condition condition, String orders, MapRowMapper<R> mapRowMapper, Class<?>... classLink) throws DaoAccessException;  /*** 连接查询** @param condition   查询条件* @param paging      分页参数* @param classLink   连接实体类型链* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public DataStore<Map<String, Object>> join(Condition condition, PagingParameter paging, Class<?>... classLink) throws DaoAccessException;/*** 连接查询** @param condition  查询条件* @param orders     排序对象* @param paging     分页参数* @param classLink  连接实体类型链* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public DataStore<Map<String, Object>> join(Condition condition, String orders, PagingParameter paging, Class<?>... classLink) throws DaoAccessException;/*** 连接查询** @param <R>          映射类型参数* @param condition    查询条件* @param mapRowMapper 行匹配对象* @param paging       分页参数* @param classLink    连接实体类型链* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public <R> DataStore<R> join(Condition condition, MapRowMapper<R> mapRowMapper, PagingParameter paging, Class<?>... classLink) throws DaoAccessException;/*** 连接查询** @param <R>          映射类型参数* @param condition    查询条件* @param orders       排序对象* @param mapRowMapper 行匹配对象* @param paging       分页参数* @param classLink    连接实体类型链* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/public <R> DataStore<R> join(Condition condition, String orders, MapRowMapper<R> mapRowMapper, PagingParameter paging, Class<?>... classLink) throws DaoAccessException;}
package com.flong.commons.persistence.dao.impl;import java.util.ArrayList;
import java.util.List;
import java.util.Map;import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;import com.flong.commons.lang.exception.DaoAccessException;
import com.flong.commons.persistence.Entity;
import com.flong.commons.persistence.bean.DataStore;
import com.flong.commons.persistence.bean.PagingParameter;
import com.flong.commons.persistence.builder.PagingSqlBuilder;
import com.flong.commons.persistence.builder.SimpleSqlBuilder;
import com.flong.commons.persistence.condition.Condition;
import com.flong.commons.persistence.dao.BaseDao;
import com.flong.commons.persistence.interfaces.ISQLQuery;
import com.flong.commons.persistence.interfaces.MapRowMapper;
import com.mchange.v2.c3p0.ComboPooledDataSource;/*** 数据查询DAO支持类** 创建日期:2012-9-26* @author wangk*/
public abstract class BaseDaoSupport implements BaseDao, InitializingBean {/** 日志对象 */private static final Logger logger = Logger.getLogger(BaseDaoSupport.class);/** 实现类日志对象 */protected final Logger log = Logger.getLogger(getClass());@Autowired protected ISQLQuery iSQLQuery;/** JDBC模版对象 */@Autowired protected JdbcTemplate jdbcTemplate;/** SQL语句参数带名称的JDBC模版对象 */protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;/** 分页SQL语句创建对象 */protected PagingSqlBuilder pagingSqlBuilder;/*** 获得JDBC模版对象** @return* 创建日期:2012-9-25* 修改说明:* @author wangk*/public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;}/*** 获得SQL语句参数带名称的JDBC模版对象** @return* 创建日期:2012-12-19* 修改说明:* @author wangk*/public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {return namedParameterJdbcTemplate;}/*** 获得分页SQL语句创建对象** @return* 创建日期:2012-10-8* 修改说明:* @author wangk*/public PagingSqlBuilder getPagingSqlBuilder() {return pagingSqlBuilder;}/*** 初始化非注入的属性* * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()* 创建日期:2012-12-19* 修改说明:* @author wangk*/@Overridepublic void afterPropertiesSet() throws Exception {//初始化namedParameterJdbcTemplatenamedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate.getDataSource());//初始化pagingSqlBuilderpagingSqlBuilder = new PagingSqlBuilder(((ComboPooledDataSource)jdbcTemplate.getDataSource()).getJdbcUrl().replaceAll("://.*$", ""));}@Overridepublic List<Map<String, Object>> search(String sql, Object... params) throws DaoAccessException {try {logger.debug(sql);return jdbcTemplate.queryForList(sql, params);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic List<Map<String, Object>> search(String sql, List<Object> params) throws DaoAccessException {return search(sql, params.toArray());}@Overridepublic List<Map<String, Object>> search(String sql, Map<String, Object> params) throws DaoAccessException {try {logger.debug(sql);return namedParameterJdbcTemplate.queryForList(sql, params);} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper, Object... params) throws DaoAccessException {List<Map<String, Object>> list = search(sql, params);if(list == null) {return null;}List<R> ret = new ArrayList<R>();for (int i = 0; i < list.size(); i++) {ret.add(mapRowMapper.mapRow(list.get(i), i));}return ret;}@Overridepublic <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper,List<Object> params) throws DaoAccessException {return search(sql, mapRowMapper, params.toArray());}@Overridepublic <R> List<R> search(String sql, MapRowMapper<R> mapRowMapper,Map<String, Object> params) throws DaoAccessException {List<Map<String, Object>> list = search(sql, params);if(list == null) {return null;}List<R> ret = new ArrayList<R>();for (int i = 0; i < list.size(); i++) {ret.add(mapRowMapper.mapRow(list.get(i), i));}return ret;}@Overridepublic DataStore<Map<String, Object>> search(String sql, PagingParameter paging,Object... params) throws DaoAccessException {try {PagingSqlBuilder pagingSqlBuilder = getPagingSqlBuilder();int records = jdbcTemplate.queryForInt(pagingSqlBuilder.getCountSql(sql), params);if(records < 0) {return null;}if(records == 0) {return new DataStore<Map<String, Object>>(records, new ArrayList<Map<String, Object>>());}return new DataStore<Map<String, Object>>(records, search(pagingSqlBuilder.getPagingSql(sql, paging), params));} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic DataStore<Map<String, Object>> search(String sql,PagingParameter paging, List<Object> params) throws DaoAccessException {return search(sql, paging, params.toArray());}@Overridepublic DataStore<Map<String, Object>> search(String sql,PagingParameter paging, Map<String, Object> params) throws DaoAccessException {try {PagingSqlBuilder pagingSqlBuilder = getPagingSqlBuilder();int records = namedParameterJdbcTemplate.queryForInt(pagingSqlBuilder.getCountSql(sql), params);if(records < 0) {return null;}if(records == 0) {return new DataStore<Map<String, Object>>(records, new ArrayList<Map<String, Object>>());}return new DataStore<Map<String, Object>>(records, search(pagingSqlBuilder.getPagingSql(sql, paging), params));} catch (Exception e) {throw new DaoAccessException(e);}}@Overridepublic <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper, PagingParameter paging, Object... params) throws DaoAccessException {DataStore<Map<String, Object>> dataStore = search(sql, paging, params);if(dataStore == null) {return null;}if(dataStore.getDatas() == null) {return new DataStore<R>(dataStore.getRecords(), null);}List<R> list = new ArrayList<R>();for (int i = 0; i < dataStore.getDatas().size(); i++) {list.add(mapRowMapper.mapRow(dataStore.getDatas().get(i), i));}return new DataStore<R>(dataStore.getRecords(), list);}@Overridepublic <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper,PagingParameter paging, List<Object> params) throws DaoAccessException {return search(sql, mapRowMapper, paging, params.toArray());}@Overridepublic <R> DataStore<R> search(String sql, MapRowMapper<R> mapRowMapper,PagingParameter paging, Map<String, Object> params) throws DaoAccessException {DataStore<Map<String, Object>> dataStore = search(sql, paging, params);if(dataStore == null) {return null;}if(dataStore.getDatas() == null) {return new DataStore<R>(dataStore.getRecords(), null);}List<R> list = new ArrayList<R>();for (int i = 0; i < dataStore.getDatas().size(); i++) {list.add(mapRowMapper.mapRow(dataStore.getDatas().get(i), i));}return new DataStore<R>(dataStore.getRecords(), list);}@Overridepublic List<Map<String, Object>> join(Condition condition,Class<?>... classLink) throws DaoAccessException {String orders = null;return join(condition, orders, classLink);}@Overridepublic List<Map<String, Object>> join(Condition condition, String orders,Class<?>... classLink) throws DaoAccessException {String sql = buildJoinSql(condition, orders, classLink);List<Map<String, Object>> result = null;if(condition == null) {result = search(sql);} else {result = search(sql, condition.getParameters());}convertJoinResult(result, classLink);return result;}@Overridepublic <R> List<R> join(Condition condition, MapRowMapper<R> mapRowMapper,Class<?>... classLink) throws DaoAccessException {return join(condition, null, mapRowMapper, classLink);}@Overridepublic <R> List<R> join(Condition condition, String orders,MapRowMapper<R> mapRowMapper, Class<?>... classLink) throws DaoAccessException {List<Map<String, Object>> list = join(condition, orders, classLink);if(list == null) {return null;}List<R> ret = new ArrayList<R>();for (int i = 0; i < list.size(); i++) {ret.add(mapRowMapper.mapRow(list.get(i), i));}return ret;}@Overridepublic DataStore<Map<String, Object>> join(Condition condition,PagingParameter paging, Class<?>... classLink) throws DaoAccessException {String orders = null;return join(condition, orders , paging, classLink);}@Overridepublic DataStore<Map<String, Object>> join(Condition condition,String orders, PagingParameter paging,Class<?>... classLink) throws DaoAccessException {PagingSqlBuilder pagingSqlBuilder = getPagingSqlBuilder();String sql = buildJoinSql(condition, orders, classLink);Object[] params = new Object[0];if(condition != null) {params = condition.getParameters();}int records = 0;try {records = jdbcTemplate.queryForInt(pagingSqlBuilder.getCountSql(sql), params);} catch (Exception e) {throw new DaoAccessException(e);}if(records < 0) {return null;}if(records == 0) {return new DataStore<Map<String, Object>>(records, new ArrayList<Map<String, Object>>());}List<Map<String, Object>> datas = null;datas = search(pagingSqlBuilder.getPagingSql(sql, paging), params);convertJoinResult(datas, classLink);return new DataStore<Map<String, Object>>(records, datas);}@Overridepublic <R> DataStore<R> join(Condition condition,MapRowMapper<R> mapRowMapper, PagingParameter paging,Class<?>... classLink) throws DaoAccessException {return join(condition, null, mapRowMapper, paging, classLink);}@Overridepublic <R> DataStore<R> join(Condition condition, String orders,MapRowMapper<R> mapRowMapper, PagingParameter paging,Class<?>... classLink) throws DaoAccessException {DataStore<Map<String, Object>> dataStore = join(condition, orders, paging, classLink);if(dataStore == null) {return null;}if(dataStore.getDatas() == null) {return new DataStore<R>(dataStore.getRecords(), null);}List<R> list = new ArrayList<R>();for (int i = 0; i < dataStore.getDatas().size(); i++) {list.add(mapRowMapper.mapRow(dataStore.getDatas().get(i), i));}return new DataStore<R>(dataStore.getRecords(), list);}/*** 构建(内)连接SQL语句** @param condition* @param orders* @param classLink* @return* 创建日期:2012-10-19* 修改说明:* @author wangk*/@SuppressWarnings({ "rawtypes", "unchecked" })private String buildJoinSql(Condition condition, String orders,Class<?>... classLink) throws DaoAccessException {try {StringBuilder sb1 = new StringBuilder();StringBuilder sb2 = new StringBuilder();for (int i = 0;i < classLink.length;i++) {Class<? extends Entity> clazz = (Class<? extends Entity>)classLink[i];SimpleSqlBuilder<? extends Entity> sqlBuilder1 = new SimpleSqlBuilder(clazz);String tableName1 = sqlBuilder1.getTableName();Map<String, String> fieldColumnMapping1 = sqlBuilder1.getFieldColumnMapping();for (String field : sqlBuilder1.getFieldColumnMapping().keySet()) {sb1.append(tableName1 +  "." + fieldColumnMapping1.get(field) + " AS " + tableName1 +  "_" + fieldColumnMapping1.get(field) + ", ");}if(i == classLink.length - 1) {break;}Class<? extends Entity> rClass = (Class<? extends Entity>)classLink[i+1];            SimpleSqlBuilder<? extends Entity> sqlBuilder2 = new SimpleSqlBuilder(rClass);String tableName2 = sqlBuilder2.getTableName();if(i == 0) {sb2.append(tableName1);}sb2.append(" JOIN " + tableName2 + " ON " + tableName1 + "." + fieldColumnMapping1.get(sqlBuilder1.getReferenceField(rClass)) + " = " + tableName2 + "." + sqlBuilder1.getReferencedColumn(rClass));}sb1.delete(sb1.length() - 2, sb1.length());String sql = "SELECT " + sb1 + " FROM " + sb2;if(condition != null) {sql += " WHERE " + condition.toSqlString();}if(orders != null) {sql += " ORDER BY " + orders;}logger.debug(sql);return sql;} catch (Exception e) {throw new DaoAccessException(e);}}/*** 转换连接查询结果,Map对象的key值为:对象名.属性名** @param result* @param classLink* 创建日期:2012-10-19* 修改说明:* @author wangk*/private void convertJoinResult(List<Map<String, Object>> result, Class<?>... classLink) throws DaoAccessException {if(CollectionUtils.isEmpty(result)) {return;}for (Map<String, Object> map : result) {try {for (Class<?> clazz : classLink) {@SuppressWarnings({ "rawtypes", "unchecked" })SimpleSqlBuilder<? extends Entity> sqlBuilder = new SimpleSqlBuilder(clazz);String tableName = sqlBuilder.getTableName();Map<String, String> fieldColumnMapping = sqlBuilder.getFieldColumnMapping();String className = clazz.getSimpleName();String variableName = className.substring(0, 1).toLowerCase() + className.substring(1);for (String field : fieldColumnMapping.keySet()) {map.put(variableName + "." + field, map.remove(tableName + "_" + fieldColumnMapping.get(field)));}}} catch (Exception e) {throw new DaoAccessException(e);}}}}
package com.flong.commons.persistence.dao.impl;import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;import javax.sql.DataSource;import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;import com.flong.commons.lang.exception.DaoAccessException;
import com.flong.commons.persistence.DbVersion;
import com.flong.commons.persistence.SqlScriptReader;
import com.flong.commons.persistence.dao.DbVersionDao;/*** 数据库版本dao实现* 创建日期:2013-1-7* @author niezhegang*/
@Repository
public class DbVersionDaoImpl extends EntityDaoSupport<DbVersion> implements DbVersionDao {/**目前支持的可升级数据库类型名称*/private String[] dbTypeNames = {"mysql"};/***/private Logger logger = Logger.getLogger(this.getClass());/**查询当前版本的SQL语句*/private String queryCurrentVersionSQL = "select id,version from "+DbVersion.TABLENAME;private String updateToNextVersionSQL = "update " + DbVersion.TABLENAME+" set version = version + 1";/*** @see com.flong.commons.persistence.dao.DbVersionDao#getCurrentVersion()* 创建日期:2013-1-7* 修改说明:* @author niezhegang*/@Overridepublic DbVersion getCurrentVersion() throws DaoAccessException {return jdbcTemplate.queryForObject(queryCurrentVersionSQL, new RowMapper<DbVersion>(){@Overridepublic DbVersion mapRow(ResultSet arg0, int arg1)throws SQLException {DbVersion version = new DbVersion();version.setId(arg0.getLong(1)); version.setVersionNumber(arg0.getInt(2)); return version;}});}/*** @see com.flong.commons.persistence.dao.DbVersionDao#updateToNextVersion()* 创建日期:2013-1-7* 修改说明:* @author niezhegang*/@Overridepublic void updateToNextVersion() throws DaoAccessException {jdbcTemplate.update(updateToNextVersionSQL);}/*** @see com.flong.commons.persistence.dao.DbVersionDao#isInitVersion()* 创建日期:2013-1-7* 修改说明:* @author niezhegang*/@Overridepublic boolean isInitVersion() throws DaoAccessException {Connection conn = null;boolean ret = false;DataSource dataSource = jdbcTemplate.getDataSource();try{conn = DataSourceUtils.getConnection(dataSource);DatabaseMetaData dbmd = conn.getMetaData();ResultSet rs = dbmd.getTables(null,null,"SYS_DBVERSION",null);if(!rs.next()){ret = true;}return ret;}catch(Exception e){throw new DaoAccessException("判断是否初始化版本失败!",e);}finally{DataSourceUtils.releaseConnection(conn, dataSource);}}/*** @see com.flong.commons.persistence.dao.DbVersionDao#executeOneSQLFile(java.io.File)* 创建日期:2013-1-8* 修改说明:* @author niezhegang*/@Overridepublic void executeOneSQLFile(File updateFile) throws DaoAccessException {SqlScriptReader scriptReader = null;try{BufferedReader bufferedReader = new BufferedReader(new FileReader(updateFile));scriptReader = new SqlScriptReader(bufferedReader);String sql = scriptReader.readOneSQL();while(sql != null){if(!StringUtils.isBlank(sql)){logger.debug(sql);jdbcTemplate.execute(sql);}sql = scriptReader.readOneSQL();}}catch(Exception exception){throw new DaoAccessException(exception);}finally{if(scriptReader != null)scriptReader.close();}}/*** @see com.flong.commons.persistence.dao.DbVersionDao#getVersionDatabaseTypeName()* 创建日期:2013-1-8* 修改说明:* @author niezhegang*/@Overridepublic String getVersionDatabaseTypeName() throws DaoAccessException{Connection conn = null;String ret = null;DataSource dataSource = jdbcTemplate.getDataSource();try{conn = DataSourceUtils.getConnection(dataSource);DatabaseMetaData dbmd = conn.getMetaData();ret = queryMatchedDbname(dbmd);return ret;}catch(Exception e){throw new DaoAccessException("获取!",e);}finally{DataSourceUtils.releaseConnection(conn, dataSource);}} /*** 根据数据库元数据匹配目前支持的数据库名* @param dbmd* @return* 创建日期:2013-1-8* 修改说明:* @author niezhegang*/private String queryMatchedDbname(DatabaseMetaData dbmd) throws SQLException{String ret = null;String driverName = dbmd.getDriverName();for(int i = 0; i < dbTypeNames.length ; i++){if(StringUtils.containsIgnoreCase(driverName, dbTypeNames[i])){ret = dbTypeNames[i];break;}}if(StringUtils.isBlank(ret))throw new RuntimeException("不支持的数据库类型:"+driverName);return ret;}}
package com.flong.commons.persistence.dao.impl;import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;import com.flong.commons.utils.ObjectUtil;/*** 数据模型基础类** 创建日期:2012-11-13* @author wangk*/
public abstract class BaseDomain implements Cloneable, Serializable {private static final long serialVersionUID = -3707046914855595598L;/*** @see java.lang.Object#toString()* 创建日期:2012-11-13* 修改说明:* @author wangk*/@Overridepublic String toString() {return ToStringBuilder.reflectionToString(this);}/*** 浅层复制(如果属性为引用类型则只复制属性的引用值)当前对象** @param <T>* @return* 创建日期:2013-1-25* 修改说明:* @author wangk*/public <T> T simpleClone() {try {@SuppressWarnings("unchecked")T ret = (T)clone();return ret;} catch (Exception e) {throw new RuntimeException(e);}}/*** 获取指定属性的值集合* @description* @param fieldNames* @return* 创建日期:2013-1-19* 修改说明:* @author wangk*/public List<Object> getFieldValues(List<String> fieldNames) {List<Object> list = new ArrayList<Object>();if(CollectionUtils.isNotEmpty(fieldNames)){for (String fieldName : fieldNames) {try {list.add(new PropertyDescriptor(fieldName, getClass()).getReadMethod().invoke(this));} catch (Exception e) {throw new RuntimeException(e);} }}return list;}/*** 将当前对象转换成属性和值的映射** @return* 创建日期:2013-1-29* 修改说明:* @author wangk*/public Map<String, Object> toFieldMapping() {Map<String, Object> entrys = new HashMap<String, Object>();Map<String, Method> readMethodMapping = ObjectUtil.getReadMethodMapping(getClass());for (String field : readMethodMapping.keySet()) {try {entrys.put(field, readMethodMapping.get(field).invoke(this));} catch (Exception e) {throw new RuntimeException(e);}}return entrys;}}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"><description>Spring MVC Configuration</description><!-- 加载配置属性文件 --><context:property-placeholder ignore-unresolvable="true" location="classpath*:/prop/DBSource.properties" /><!-- 使用Annotation自动注册Bean,只扫描@Controller --><context:component-scan base-package="com.flong"><!-- base-package 如果多个,用“,”分隔 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 设置请求映射编码,解决@ResponseBody乱码问题 --><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><bean class="org.springframework.http.converter.StringHttpMessageConverter"><property name="supportedMediaTypes"><list><value>text/plain;charset=UTF-8</value><!--    <value>text/html;charset=UTF-8</value> --><value>application/json;charset=UTF-8</value>   </list></property></bean><ref bean="mappingJacksonHttpMessageConverter"/> </list></property></bean><!-- 处理JSON数据转换的 --> <!-- 为了处理返回的JSON数据的编码,默认是ISO-88859-1的,这里把它设置为UTF-8,解决有乱码的情况 --><bean id="mappingJacksonHttpMessageConverter"   class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">   <property name="supportedMediaTypes">    <list>    <!--   <value>text/html;charset=UTF-8</value> --><value>application/json;charset=UTF-8</value></list>    </property>    </bean>  <!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping --><mvc:annotation-driven/><!-- 对静态资源文件的访问, 将无法mapping到Controller的path交给default servlet handler处理 --><mvc:default-servlet-handler/><!-- 视图文件解析配置 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="${web.view.prefix}"/><property name="suffix" value="${web.view.suffix}"/></bean><!-- 定义无Controller的path<->view直接映射 --><mvc:view-controller path="/" view-name="redirect:${web.view.index}"/><!-- 上传文件拦截,设置最大上传文件大小   10M=10*1024*1024(B)=10485760 bytes -->  <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  <property name="maxUploadSize" value="${web.maxUploadSize}" />  </bean><!-- 这个映射配置主要是用来进行静态资源的访问对静态资源文件的访问, 将无法mapping到Controller的path交给default servlet handler处理 <mvc:default-servlet-handler/>同等如下配置<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/> <mvc:resources mapping="/css/**" location="/css/" />  <mvc:resources mapping="/**/**" location="/" />  <mvc:resources mapping="/WEB-INF/view/**" location="/WEB-INF/view/" />  --></beans>



项目图解分析简单说明









代码下载 http://download.csdn.net/detail/jilongliang/9321581

SpringMVC jdbctemplate实现底层架构封装相关推荐

  1. 媒体播放器三大底层架构_射手科技zz

    媒体播放器三大底层架构_射手科技 自射手影音推出以来,生活中越来越多的时间开始被代码和各种Bug-Fix淹没.埋头在田里太久,常常会在一时之间忘记身处何方.所以偶尔上来透透气,顺便将一些经验和心得与大 ...

  2. 操作系统底层架构与内核设计及实现原理

    一.操作系统CPU 1.CPU简介 CPU的全称是CentrolProcessingUnit,它是你的电脑中最硬核的组件,这种说法一点不为过. CPU是能够让你的计算机叫 计算机 的核心组件,但是它却 ...

  3. 一份职位信息的精准推荐之旅,从AI底层架构说起

    整理 | 夕颜 出品 | AI科技大本营(ID:rgznai100) [导读]也许,每天早上你的邮箱中又多了一封职位推荐信息,点开一看,你可能发现这些推荐正合你意,于是按照这些信息,你顺利找到一份符合 ...

  4. 字节跳动推荐平台技术公开,项亮:底层架构有时比上层算法更重要

    允中 发自 凹非寺 量子位 报道 | 公众号 QbitAI 字节跳动已正式吹响进军云计算市场号角. 12月2日,火山引擎全系列云产品亮相,共推出了78项云产品服务,涵盖云基础.视频及内容分发.数据中台 ...

  5. Azure底层架构的初步分析

    之所以要写这样的一篇博文的目的是对于大多数搞IT的人来说,一般都会对这个topic很感兴趣,因为底层架构直接关乎到一个公有云平台的performance,其实最主要的原因是我们的客户对此也非常感兴趣, ...

  6. 资深数据大牛《教你如何从零开始做大数据底层架构》!(转)

    http://www.toutiao.com/a6475623583879004685/?tt_from=weixin&utm_campaign=client_share&app=ne ...

  7. mysql多大_洞悉MySQL底层架构:游走在缓冲与磁盘之间

    提起MySQL,其实网上已经有一大把教程了,为什么我还要写这篇文章呢,大概是因为网上很多网站都是比较零散,而且描述不够直观,不能系统对MySQL相关知识有一个系统的学习,导致不能形成知识体系.为此我撰 ...

  8. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  9. 打造自己的分布式搜索引擎底层架构(非Lucene)

    打造自己的分布式搜索引擎底层架构(非Lucene) 大家知道,搜索引擎技术不仅仅是类似百度首页的应用,还可以衍生出数据分析工具,商务智能工具等许多有卖点的应用,甚至是社会化关系通道的发现. 甚至这些非 ...

最新文章

  1. 【ES6】JS类的用法class
  2. Ubuntu下设置电信拨号上网(10.04版)
  3. Javascript Step by Step - 03
  4. 事务隔离级别,看这一篇就够了
  5. java中 set集合_第8篇 Java中的集合(Set)
  6. 【Python】一种pyahocorasick库安装方法
  7. python-布尔数据
  8. w我的页面显示服务器错误,vue项目,在本地打开一个页面正常,部署到服务器就会出现 404Not Found 求解?...
  9. ui自动化分享ppt_全面迎接自动化!微软公布RPA价格,将于4月2日正式上市
  10. awx文件解析,运用NCL处理风云卫星2E的AWX格式数据的总结
  11. Java中 IO 常用操作
  12. 电脑32位和64位有什么区别
  13. 2019牛客暑期多校训练营(第一场)(B、C、E、F、H、I题待补、J)
  14. 直播平台必备-百度音视频直播 LSS
  15. 2019房企变局:周期轮回,“踏雪寻梅”
  16. 网络入侵检测系统之Suricata(三)--日志代码详解
  17. python 复数补充
  18. 自主研发-谭八爷代理下单系统开发
  19. 地址总线、物理地址、虚拟地址讲解
  20. Xcode中常见英文

热门文章

  1. Coding and Paper Letter(七十六)
  2. ----- 前端面试题 -----
  3. 任务一深度思考之测试
  4. python统计水仙花数个数_Python一句代码实现找出所有水仙花数的方法
  5. 深入实践 ES6 Proxy Reflect
  6. 怎样创建谷歌Merchant Center和链接到购物广告
  7. 9个最佳新闻聚合器网站(+如何构建自己的网站)
  8. SNMP采集测试工具使用方法
  9. snmp连接工具_运维工程师不可错过的2020年值得关注的综合性网络监控工具
  10. 卡尔卡西25首练习曲简析-音阶与音程训练