java工具类之MySQL基于JdbcTemplate的单表操作工具类

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

/* _知识点一:
 * newInstance(): 弱类型。低效率。只能调用无参构造。
	使用newInstance有两个前提:
	1.这个类已经加载;
	2.这个类已经连接了。
	同时,newInstance()是实现IOC、反射、依赖倒置 等技术方法的必然选择,new只能实现具体类的实例化,
	不适合于接口编程。类里面就是通过这个类的默认构造函数构建了一个对象,如果没有默认构造函数就抛
	出InstantiationException, 如果没有访问默认构造函数的权限就抛出IllegalAccessException
 * new: 强类型。相对高效。能调用任何public构造。
 * 
 *  _知识点二:
 *  ArrayList是Java中比较常用的一个类,它是基于数组实现,非线程安全,可快速随机访问List中的元素。
 *	ArrayList具有动态扩容的机制,每次在添加元素时,都会判断容量是否够用,如果不够用,则需要扩容。
 *	JDK1.8中,ArrayList的初始容量为0,第一次添加元素时,会将容量设置为10,如果容量不够,则每次会扩大50%
 */
/**
 * @author ZQL 基于JdbcTemplate的单表操作工具类(MySql)_20190720
 * @param <T> T泛型,代表实体类
 */
public class MySQLEntityUtil<T> {

	public static class Operator {
		
		final public static String __thePreFixForLike = UUID.randomUUID().toString().substring(0, 2)
				+ UUID.randomUUID().toString().substring(10, 12);
		
		/**
		 * @param keyWord
		 * @return
		 */
		public static String like (String keyWord) {
			return __thePreFixForLike + keyWord;
		}
	}
	/**
	 * 实体成员数据类型(key)与数据库数据类型(value)键值对
	 */
	private static Map<String, String> t = null;
	static {
		t=new HashMap<String,String>();
		t.put("java.lang.Long", "BIGINT");
		t.put("java.lang.String", "VARCHAR");
		t.put("java.lang.Integer", "INTEGER");
		t.put("java.util.Date", "DATE");
		t.put("java.lang.Double", "DOUBLE");
		t.put("java.lang.Boolean", "BIT");
		// DECIMAL(P,D)表示列可以存储D位小数的P位数。十进制列的实际范围取决于精度和刻度。
		t.put("java.math.BigDecimal", "DECIMAL(18,8) DEFAULT(0)");
	}
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(MySQLEntityUtil.class);
	/**
	 * 声明一个泛型类
	 */
	private Class<T> entityClass = null;
	/**
	 * 表名
	 */
	private String tableName = null;
	/**
	 * 主键对应的实体的成员变量名称
	 */
	private String primaryKeyFieldName = null;
	/**
	 * 数据库主键列名
	 */
	private String primaryKeyColumnName = null;
	/**
	 * 实体成员变量名称(key)与数据库列名(value) 键值对
	 */
	private Map<String, String> fieldColumnMap = null;
	/**
	 * 实体成员变量名称数组
	 */
	private String[] fieldNames = null;
	/**
	 * 数据库列名数组
	 */
	private String[] columnNames = null;
	/**
	 * 经过处理的数据库列名,可直接用于拼接sql语句(例如 MySql 关键字desc -> `desc` )
	 */
	private String[] columnNamesForSQL = null;
	/**
	 * insert的sql语句
	 */
	private String sqlForAdd = null;
	/**
	 * 实体成员变量名称(key)与数据类型(value)键值对
	 */
	private Map<String, Class<?>> fieldType = new HashMap<String, Class<?>>();

	/**
	 * @param entityClass 添加了JPA注解的类
	 */
	public MySQLEntityUtil(Class<T> entityClass) {
		this.entityClass = entityClass;
		this.tableName = this.entityClass.getAnnotation(Table.class).name();
		parseFieldAndColumn();
	}
	
	/**
	 * 	解析字段和列名,并存入数组,同时拼接数据库插入语句
	 */
	private void parseFieldAndColumn() {
		this.fieldColumnMap = new LinkedHashMap<String, String>();

		getColumnFromSelfAndParent(this.entityClass, fieldColumnMap);

		List<String> fieldList = new ArrayList<String>();
		List<String> columnList = new ArrayList<String>();

		StringBuilder sqlForAdd = new StringBuilder("INSERT INTO `");

		sqlForAdd.append(this.tableName).append("`(");
		StringBuilder chars = new StringBuilder();

		int c = this.fieldColumnMap.size();
		this.fieldNames = new String[c];
		this.columnNames = new String[c];
		this.columnNamesForSQL = new String[c];
		// 索引计数器
		int counter = 0;
		boolean isFirst = true;
		for (Map.Entry<String, String> e : this.fieldColumnMap.entrySet()) {
			this.fieldNames[counter] = e.getKey();
			this.columnNames[counter] = e.getValue();
			this.columnNamesForSQL[counter] = "`" + e.getValue() + "`";
			fieldList.add(this.fieldNames[counter]);
			columnList.add(this.columnNames[counter]);
			if (!this.columnNames[counter].equals(this.primaryKeyColumnName)) {
				if (isFirst) {
					isFirst = false;
					sqlForAdd.append("`" + this.columnNames[counter] + "`");
					chars.append("?");
				} else {
					sqlForAdd.append(",").append("`" + this.columnNames[counter] + "`");
					chars.append(",?");
				}
			}
			++counter;
		}
		sqlForAdd.append(")VALUES(").append(chars).append(") ");
		this.sqlForAdd = sqlForAdd.toString();
	}

	/**
	 * 获取列
	 * 
	 * @param clz
	 * @param fieldColumnMap
	 */
	private void getColumn(Class<?> clz, Map<String, String> fieldColumnMap) {
		Field[] fields = clz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			Column ann = field.getAnnotation(Column.class);
			if (ann != null) {
				String fieldName = field.getName();
				String columnName = ann.name();
				fieldColumnMap.put(fieldName, columnName);
				this.fieldType.put(fieldName, field.getType());
				Id idAnn = field.getAnnotation(Id.class);
				if (idAnn != null) {
					this.primaryKeyFieldName = field.getName();
					this.primaryKeyColumnName = ann.name();
				}
			}
		}
	}

	/**
	 * 获取继承的父类
	 * 
	 * @param clz
	 * @param fieldColumnMap
	 */
	private void getColumnFromSelfAndParent(Class<?> clz, Map<String, String> fieldColumnMap) {
		getColumn(clz, fieldColumnMap);
		if (clz.getSuperclass() != Object.class) {
			getColumnFromSelfAndParent(clz.getSuperclass(), fieldColumnMap);
		}
	}

	/**
	 * 执行Entity getXxx方法
	 * 
	 * @param fieldName 成员变量名称
	 * @param obj 方法执行目标对象
	 * @return
	 * @throws Exception
	 */
	public Object invokeGet(String fieldName, Object obj) throws Exception {
		String methodName = "get".concat(upperFirstChar(fieldName));
		Method method = this.entityClass.getMethod(methodName);
		return method.invoke(obj);
	}

	/**
	 * 执行Entity setXxxx方法
	 * 
	 * @param fieldName 成员变量名称
	 * @param obj 方法执行目标对象
	 * @param value set设置值
	 * @throws Exception
	 */
	public void invokeSet(String fieldName, Object obj, Object value) throws Exception {
		String methodName = "set".concat(upperFirstChar(fieldName));
		Method method = this.entityClass.getMethod(methodName, this.fieldType.get(fieldName));
		method.invoke(obj, new Object[] { value });
	}

	/**
	 * 首字母小写转大写
	 * 
	 * @param str
	 * @return
	 */
	public static String upperFirstChar(String str) {
		byte[] items = str.getBytes();
		if (items[0] >= 'a' && items[0] <= 'z') {
			items[0] = (byte) ((char) items[0] - 'a' + 'A');
		}
		return new String(items);
	}

	/**
	 * 向数据库插入一条数据
	 * 
	 * @param entity 添加了JPA注解的实体
	 * @param jdbcTemplate JdbcTemplate实例
	 * @return 返回主键
	 */
	public long insert(T entity, JdbcTemplate jdbcTemplate) {
		List<Object> params = new ArrayList<Object>();
		try {
			for (int i = 0; i < this.columnNames.length; i++) {
				if (this.columnNames[i].equals(this.primaryKeyColumnName)) {
					continue;
				}
				Object value = this.invokeGet(this.fieldNames[i], entity);
				params.add(value);
			}
		} catch (Exception e) {
			logger.error(e);
			return -1L;
		}
		if (logger.isDebugEnabled()) {
			String sepa = System.getProperty("line.separator");
			StringBuilder paramsValue = new StringBuilder();
			Object[] p = params.toArray();
			for (int i = 0; i < p.length; i++) {
				paramsValue.append(i).append(":").append(p[i]).append(sepa);
			}
			logger.debug(sepa + "MySQLEntityUtil.class long_add(" + this.getClass() + ",JdbcTemplate)" + sepa + "-SQL:"
					+ this.sqlForAdd + "\\n" + "-params:" + sepa + " " + paramsValue + "  >------------");
		}
		KeyHolder keyHolder = new GeneratedKeyHolder();
		jdbcTemplate.update(new PreparedStatementCreator() {
			@Override
			public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
				PreparedStatement ps = con.prepareStatement(sqlForAdd,PreparedStatement.RETURN_GENERATED_KEYS);
				for (int i = 1; i <= params.size(); i++) {
					ps.setObject(i, params.get(i-1));
				}
				return ps;
			}
		},keyHolder);
		return keyHolder.getKey().longValue();
	}

	/**
	 * 根据主键id查询数据
	 * 
	 * @param id 主键
	 * @param jdbcTemplate JdbcTemplate实例
	 * @return
	 */
	public T getById(long id, JdbcTemplate jdbcTemplate) {
		// 声明实体类
		T entity = null;
		String sql = "SELECT * FROM `" + this.tableName + "` WHERE `" + this.primaryKeyColumnName + "`=" + id;
		entity = jdbcTemplate.query(sql, new ResultSetExtractor<T>() {
			@Override
			public T extractData(ResultSet rs) throws SQLException, DataAccessException {
				T entity = null;
				try {
					// 实例化泛型类
					entity = entityClass.newInstance();
					for (int i = 0; i < fieldNames.length; i++) {
						Object value = rs.getObject(columnNames[i]);
						// 设置实体类的变量值
						invokeSet(fieldNames[i], entity, value);
					}
				} catch (InstantiationException e) {
					// 没有默认构造函数
					logger.error("The error message:There is no default constructor !");
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// 不能访问默认构造函数
					logger.error("The error message:No access to the default constructor !");
					e.printStackTrace();
				} catch (Exception e) {
					logger.error(e);
					e.printStackTrace();
				}
				return entity;
			}
		});
		return entity;
	}

	/**
	 * 根据实体类设置的条件查询数据,所有条件以AND连接
	 * 
	 * @param conditions 条件(null字段自动被忽略)
	 * @param jdbcTemplate JdbcTemplate实例
	 * @param pageSize 要查询的数据条数
	 * @param pageNum 要查询的数据起始页
	 * @param orderBy orderBy可选条件(默认按主键降序排列)
	 * @return
	 */
	public List<T> listObjects(T conditions, JdbcTemplate jdbcTemplate, final int pageSize, int pageNum, String orderBy) {
		// where 1=1 是为了避免where 关键字后面的第一个词直接就是 “and”而导致语法错误。
		StringBuilder sql = new StringBuilder("SELECT * FROM `").append(this.tableName).append("` WHERE 1=1 ");
		List<Object> params = new ArrayList<Object>();
		try {
			// 遍历列表
			for (int i = 0; i < this.columnNames.length; i++) {
				// 获取实体类的变量值
				Object value = this.invokeGet(this.fieldNames[i], conditions);
				if (value != null) {
					String op = "=";
					// 如果是字符串,并且有like关键字标识,则将操作符设置为like
					if (value instanceof String && ((String) value).startsWith(Operator.__thePreFixForLike)) {
						op = " like ";
						value = ((String) value).substring(Operator.__thePreFixForLike.length());
					}
					// 拼接条件
					sql.append(" AND `").append(this.columnNames[i]).append("`").append(op).append("?");
					params.add(value);
				}
			}
		} catch (Exception e) {
			logger.error(e);
			return null;
		}

		if (orderBy != null) {
			// 自定义order by
			sql.append(" ORDER BY ").append(orderBy);
		} else if (this.primaryKeyColumnName != null) {
			// 默认按主键降序排列
			sql.append(" ORDER BY ").append(this.primaryKeyColumnName).append(" DESC ");
		}

		sql.append(" LIMIT ? OFFSET ?");
		params.add(pageSize);
		params.add(pageSize * (pageNum - 1));

		List<T> list = null;
		list = jdbcTemplate.query(sql.toString(), params.toArray(new Object[params.size()]),
				new ResultSetExtractor<List<T>>() {
					public List<T> extractData(ResultSet rs) throws SQLException, DataAccessException {
						int initNum = 0;
						if (pageSize > 100) {
							initNum = 100;
						} else if(pageSize > 1000){
							initNum = 1000;
						}
						/*
						 * ArrayList的初始容量为0,第一次添加元素时,会将容量设置为10,如果容量不够,则每次会扩大50%,
						 * 而ArrayList每次扩容都需要做一次数组拷贝,如果是反复扩容,肯定会对程序的运行效率产生影响。
						 * _所以在初始化ArrayList的时候,尽量设置初始化容量,避免其过多扩容影响效率。
						 */
						List<T> list = new ArrayList<T>(initNum);
						try {
							while (rs.next()) {
								T entity = entityClass.newInstance();
								for (int i = 0; i < fieldNames.length; i++) {
									String s = columnNames[i];
									Object value = rs.getObject(s);
									String f = fieldNames[i];
									// 设置实体类的变量值
									invokeSet(f, entity, value);
								}
								list.add(entity);
							}
						} catch (Exception e) {
							logger.error(e);
							e.printStackTrace();
						}
						return list;
					}
				});
		return list;
	}

	/**
	 * 根据实体类设置的条件查询数据总数,所有条件以AND连接
	 * 
	 * @param conditions 条件(null字段自动被忽略)
	 * @param jdbcTemplate JdbcTemplate实例
	 * @return
	 */
	public int count(T conditions, JdbcTemplate jdbcTemplate) {
		StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM `").append(this.tableName).append("` WHERE 1=1 ");
		List<Object> params = new ArrayList<Object>();
		try {
			for (int i = 0; i < this.columnNames.length; i++) {
				// 获取实体类的变量值
				Object value = this.invokeGet(this.fieldNames[i], conditions);
				if (value != null) {
					String op = "=";
					// 如果是字符串,并且有like关键字标识,则将操作符设置为like
					if (value instanceof String && ((String) value).startsWith(Operator.__thePreFixForLike)) {
						op = " like ";
						value = ((String) value).substring(Operator.__thePreFixForLike.length());
					}
					sql.append(" AND `").append(this.columnNames[i]).append("`").append(op).append("?");
					params.add(value);
				}
			}
		} catch (Exception e) {
			logger.error(e);
			return -1;
		}
		return jdbcTemplate.queryForObject(sql.toString(), params.toArray(), Integer.class);
	}

	/**
	 * 根据更新值和条件进行更新数据
	 * 
	 * @param values 更新的值(实体调用setXXX方法的字段)
	 * @param conditions 条件(实体调用setXXX方法的字段)
	 * @param jdbcTemplate JdbcTemplate实例
	 * @return
	 */
	public int update(T values, T conditions, JdbcTemplate jdbcTemplate) {
		StringBuilder sql = new StringBuilder();
		sql.append("UPDATE `").append(this.tableName).append("` SET `").append(this.primaryKeyColumnName).append("`=`")
			.append(this.primaryKeyColumnName).append("`");
		StringBuilder condition = new StringBuilder(" WHERE 1=1 ");
		List<Object> paramsValue = new ArrayList<Object>(this.columnNames.length * 2);
		List<Object> paramsCondition = new ArrayList<Object>(this.columnNames.length);

		try {
			// 遍历实体成员变量名称(key)与数据库列名(value) 键值对
			for (Map.Entry<String, String> e : this.fieldColumnMap.entrySet()) {
				String fieldName = e.getKey();
				String columnName = e.getValue();
				Object value = this.invokeGet(fieldName, values);
				Object con = this.invokeGet(fieldName, conditions);
				if (value != null) {
					paramsValue.add(value);
					sql.append(",`").append(columnName).append("`=?");
				}
				if (con != null) {
					String op = "=";
					// 如果是字符串,并且有like关键字标识,则将操作符设置为like
					if (con instanceof String && ((String) con).startsWith(Operator.__thePreFixForLike)) {
						op = " like ";
						con = ((String) con).substring(Operator.__thePreFixForLike.length());
					}
					condition.append(" AND `").append(columnName).append("`").append(op).append("?");
					paramsCondition.add(con);
				}
			}
		} catch (Exception e) {
			logger.error(e);
		}
		paramsValue.addAll(paramsCondition);
		return jdbcTemplate.update(sql.append(condition).toString(), paramsValue.toArray());
	}

	/**
	 * 根据条件删除数据
	 * 
	 * @param conditions 条件(实体调用setXXX方法的字段)
	 * @param jdbcTemplate JdbcTemplate实例
	 * @return
	 */
	public int delete(T conditions, JdbcTemplate jdbcTemplate) {
		StringBuilder sql = new StringBuilder();
		sql.append("DELETE FROM `").append(this.tableName).append("` WHERE 1=1 ");
		List<Object> params = new ArrayList<Object>(this.columnNames.length);
		try {
			for (Map.Entry<String, String> e : this.fieldColumnMap.entrySet()) {
				String fieldName = e.getKey();
				String columnName = e.getValue();
				Object con = this.invokeGet(fieldName, conditions);
				if (con != null) {
					String op = "=";
					// 如果是字符串,并且有like关键字标识,则将操作符设置为like
					if (con instanceof String && ((String) con).startsWith(Operator.__thePreFixForLike)) {
						op = " like ";
						con = ((String) con).substring(Operator.__thePreFixForLike.length());
					}
					sql.append(" AND `").append(columnName).append("`").append(op).append("?");
					params.add(con);
				}
			}
		} catch (Exception e) {
			logger.error(e);
		}
		return jdbcTemplate.update(sql.toString(), params.toArray());
	}

	public Class<?> getEntityClass() {
		return entityClass;
	}

	public Map<String, String> getFieldColumnMap() {
		return fieldColumnMap;
	}

	public String getPrimaryKeyColumnName() {
		return primaryKeyColumnName;
	}

	public String getPrimaryKeyFieldName() {
		return primaryKeyFieldName;
	}

	public String getTableName() {
		return tableName;
	}

	public String[] getColumnNames() {
		return columnNames;
	}

	public String[] getFieldNames() {
		return fieldNames;
	}
	
}
发布了55 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/mr_zql/article/details/96834058
今日推荐