谢邀,人在美国,刚下飞机,博士学位,年薪百万
之前在国内初学java时写过一篇关于DBUtil简单CRUD的帖子:
/driverTu/codes/0m2e3nvo6wb89ha7tjrgp24
后来随着自身对java的深入学习了解,特别是对反射的原理、PreparedStatement、Druid有了比较深刻的印象,在新的实战项目中针对DBUtils的有了更好的集成方案,以下就是这个方案中的部分内容。其中有遇到一些坑和思考的转折点会在文中靠回忆稍微提一下。
一、依赖:
国际惯例,改造一个这种插件性质的工具先在中添加:(demo是1.6)
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
二、添加的一些基础资源操作类(可能不是最优解,但也够用了)
这个名字就叫好了,注意添加Slf4j,这个很重要,还用到了Druid获取数据库连接,其中要注意Druid的数据库连接配置,比如 :testOnBorrow: true、validationQuery: SELECT 1 FROM DUAL、removeAbandoned: true、removeAbandonedTimeout: 1800、keepAlive: true等配置信息。
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
@Slf4j
public class BaseOperation {
// 创建一个threadlocal对象
public static ThreadLocal<Connection> container = new ThreadLocal<Connection>();
// 从当前线程中提取connection
public static Connection getContainer() throws SQLException{
Connection conn = ();
if(null == conn) {
// 获取数据库连接(共用框架内alibaba的druid)
DruidDataSource dataSource = ("dataSource");
conn = ();
(conn);
}
return conn;
}
// 开启事务
public static Connection startTransaction() {
try {
Connection conn = getContainer();
(false); // 关闭自动提交
return conn;
} catch (SQLException e) {
();
("开启事务异常", e);
}
return null;
}
// 提交事务
public static void commit() {
try {
Connection conn = getContainer();
if(null != conn)
();
} catch (SQLException e) {
();
("提交事务异常", e);
}
}
// 回滚事务
public static void rollback(){
try {
Connection conn = getContainer(); //检查当前线程是否存在连接
if(conn != null) {
(); //回滚事务
}
} catch (SQLException e) {
();
("回滚事务异常", e);
}
}
// 关闭连接
public static void close() {
try {
Connection conn = getContainer();
if (null != conn) // 关闭连接对象
();
} catch (SQLException e) {
();
("关闭连接异常", e);
} finally {
(); // 从当前线程移除连接,避免造成内存泄漏
}
}
}
三、可以添加几个实用方法的工具类了——DBUtil
1、第一个方法是批量删除
2、第二个方法是批量增加(还有查询和修改的没用到,以后再写吧),还附带一个打印PreparedStatement的SQL的插件吧!
也就这么2个方法,哈哈哈
===第一个方法:
// 批量删除数据(condition:Json型条件参数,tableName:数据库表名)
public static String deleteBatch(String tableName, JSONObject condition) {
// DELETE FROM Table_name WHERE ID = ?
// 将Json型条件组装成and语句
int num = 0;
PreparedStatement pst = null;
try {
StringBuilder sb = new StringBuilder();
String andStr = " AND ";
String conditionStr = "";
if(condition != null) {
// 取值,强行手动添加
for (String key : ()) {
(key + " = " + (key) + andStr);
}
// 去掉末尾的and
conditionStr = (0, () - ());
}
String sql = ("DELETE FROM %s WHERE %s", tableName, conditionStr);
("=##========"+sql);
pst = ().prepareStatement(sql);
num = ();
();
();
} catch (Exception e) {
();
();
("批量执行异常", e);
} finally {
if(null != pst) {
try {
(pst);
} catch (SQLException e) {
();
("关闭PreparedStatement异常", e);
}
}
();
}
return "受影响记录数为:" + num;
}
===第2个方法:
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
* 批量插入数据【未关闭资源】
* @param tablename 表名
* @param seqName 序列名(插入新数据,Oracle需要给一个序列用来主键自增)
* @param paramList 字段名--对应数据值【键值对】,【反射】实现,需要给一个注解给实体属性
* @param typeMap 字段名--对应beantype类型【键值对】
*/
private static BaseReturn doInsertBatch(String tablename, String seqName,
List<Map<String, Object>> paramList,
Map<String, String> typeMap
) {
PreparedStatement pst = null;
int num = 0;
// 预处理SQL语句
StringBuilder n = new StringBuilder();
StringBuilder v = new StringBuilder();
for (String k : (0).keySet()) {
("?,");
(k + ",");
}
SessionUser sessionUser = ();
Long currUserOid = sessionUser==null?null:();
String currUserName = sessionUser==null?null:();
String sql = ("INSERT INTO %s (OID, CREATED_DATE, NUMBER, %s) "
+ "VALUES(%, s%, %d, %s)",
tablename,
().subSequence(0, () - 1),
seqName,
"to_date('" + (new Date()) + "','yyyy-mm-dd hh24:mi:ss')",
0,
().subSequence(0, () - 1)
);
try {
pst = ().prepareStatement(sql);
// 这是打印pst中的SQL语句的
// pst = new LoggableStatement((), sql);
for (Map<String, Object> param : paramList) {
int i = 0;
for (String key : ()) {
Object valueObj = (key);
String typeObj = (key);
// 因为java中的日期与数据库日期引用库不同,所以根据javaBeanType来做下转换,避免出现无效的列类型
if("String".equals(typeObj)){
// 转义单引号
String str = valueObj==null?"":(valueObj);
(++i, str);
}else if("Date".equals(typeObj)) {
date = new (((Date)valueObj).getTime());
(++i, date);
}else if("Long".equals(typeObj)){
(++i, (long)valueObj);
}else if("Double".equals(typeObj)){
(++i, (double)valueObj);
}else if("boolean".equals(typeObj)){
(++i, (boolean)valueObj);
}else {
(++i, valueObj);
}
}
// 打印实际执行的SQL语句
// ("Executing SQL: "+((LoggableStatement)pst).getQueryString());
();
num ++;
// 已验证最佳效率:一万五
if(num % 15000 == 0) {
("=##========():" + num);
();
();
();
}
}
();
// ();
();
// num = ();
return new BaseReturn("受影响记录数为:" + num);
} catch (Exception e) {
num = -1;
();
();
("批量执行异常", e);
return new BaseReturn(BaseResultCode.ERROR_CODE_500, "业务执行异常" + ());
} finally {
if(null != pst) {
try {
(pst);
} catch (SQLException e) {
();
("关闭PreparedStatement异常", e);
}
}
// ();
();
}
}
/**
* 批量插入数据
* @param tableName 数据待插入的数据库表名
* @param seqName 待插入数据给定使用的数据库索引名
* @param dataList 传入的数据,哪些要插入哪些不要可以通过实体属性上的注解来决定【反射】
* @param handDefaultColumnValue 【此方式会被datalist内同名变量值覆盖】
* handDefaultColumnValue内有一个一一对应的beantype,获取方式为数据库字段名+后缀
* 也可设置手动属性isHandOperat=false,来通过反射控制
*/
@SuppressWarnings("hiding")
public static <T> BaseReturn insertBatch(String tableName, String seqName,
List<T> dataList, JSONObject handDefaultColumnValue){
// rejectValueToMap
// 将属性中的值加工成SQL能用的值(beanName-value的键值对)
// 原理:根据代码生成器的规则,可以由属性反推属性对应字段名应该是什么
try {
Field[] fields = (0).getClass().getDeclaredFields();
List<String> keyColumnNameList = new ArrayList<String>();
List<String> fieldNameList = new ArrayList<String>();
Map<String, String> columnTypeValueModel = new HashMap<String, String>();
for (Field field : fields) {
ExcelProperty epj = ();
DBUtilProperty dbupj = ();
if (dbupj != null && ()) {
// 手动添加部分属性-值的键值对(手动给没有配置@ExcelProperty的isImport属性的值及父类basePojo的一些值)
} else if(null == epj || !()) {
// 排除部分不导入数据
continue;
}
(true);
// key
String columnName = getColumnNameStr(());
(columnName);
// fieldName
(());
// fieldType
String beanTypeStr = ().toString();
beanTypeStr = ((".")+1);
(columnName, beanTypeStr);
}
if((keyColumnNameList)) {
return new BaseReturn(BaseResultCode.ERROR_CODE_500,
"实体属性无ExcelProperty或DBUtilProperty注解配置");
}
// 强行手动给默认值的
Map<String, Object> columnNameValueModel = new HashMap<String, Object>();
if(handDefaultColumnValue != null) {
// 取值,强行手动添加
for (String key : ()) {
// 这里需要对handDefaultColumnValue进行规则验证:要
if(("_TYPETT")) {
continue;
}
if(null != (key) && null == (key+"_TYPETT")) {
return new BaseReturn(BaseResultCode.ERROR_CODE_500,
"手动传入的值必须带各自的类型beantype");
}
(key, (key));
(key, ((key+"_TYPETT")));
}
}
List<Map<String, Object>> listColumnNameValue = new ArrayList<Map<String,Object>>();
for (T t : dataList) {
// 自动扫描入参对象类的导入数据属性-值的键值对
Map<String, Object> columnNameValue = new HashMap<String, Object>();
(columnNameValueModel);
for (int j = 0; j < (); j++) {
Object obj = getFieldValue((j), t);
((j), obj);
}
// for (String handKey : handList) {
// (getColumnValueStr(handKey), getFieldValue(handKey, t));
// }
(columnNameValue);
}
// 可能多余
if((listColumnNameValue)) {
return new BaseReturn(BaseResultCode.ERROR_CODE_500,
"暂时不允许插入空或无效数据");
}
BaseReturn retMsg = doInsertBatch(tableName, seqName, listColumnNameValue, columnTypeValueModel);
return retMsg;
} catch (Exception e ) {
();
}
return new BaseReturn(BaseResultCode.ERROR_CODE_500, "批量操作错误");
}
// 通过反射获取要填充的数据值
@SuppressWarnings("hiding")
private static <T> Object getFieldValue(String fieldName, T t) {
Method m = null;
try {
fieldName = (
(0, 1),
(0, 1).toUpperCase());
m = ().getMethod("get"+fieldName);
return (t);
} catch (NoSuchMethodException e) {
// ();
try {
m = ().getMethod("is"+fieldName);
return (Boolean)(t);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
();
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
();
} catch (Exception e) {
();
}
return null;
}
// 获取属性对应的数据库字段名称(考录项目限定规则)
private static String getColumnNameStr(String fieldName) {
int len = ();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
char curr = (i);
// 反推字段名称
if ((curr)) {
// 如果是大写就在前面加下划线(考录项目限定规则)
("_");
}
((curr));
}
return ();
}
PreparedStatement的SQL的插件:
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
* 项目名称:CFR
* 类名称:LoggableStatement
* 类描述:扩展PreparedStatement,以便输出执行的sql语句,即sql日志
*/
public class LoggableStatement implements PreparedStatement {
/** used for storing parameter values needed for producing log */
private ArrayList parameterValues;
/** the query string with question marks as parameter placeholders */
private String sqlTemplate;
/** a statement created from a real database connection */
private PreparedStatement wrappedStatement;
public LoggableStatement(Connection connection, String sql)
throws SQLException {
// use connection to make a prepared statement
wrappedStatement = (sql);
sqlTemplate = sql;
parameterValues = new ArrayList();
}
private void saveQueryParamValue(int position, Object obj) {
String strValue;
if (obj instanceof String || obj instanceof Date) {
// if we have a String, include '' in the saved value
strValue = "'" + obj + "'";
} else {
if (obj == null) {
// convert null to the string null
strValue = "null";
} else {
// unknown object (includes all Numbers), just call toString
strValue = ();
}
}
// if we are setting a position larger than current size of
// parameterValues, first make it larger
while (position >= ()) {
(null);
}
// save the parameter
(position, strValue);
}
// 这一步是对ArrayList与sql进行处理,输出完整的sql语句
public String getQueryString() {
int len = ();
StringBuffer t = new StringBuffer(len * 2);
if (parameterValues != null) {
int i = 1, limit = 0, base = 0;
while ((limit = ('?', limit)) != -1) {
((base, limit));
((i));
i++;
limit++;
base = limit;
}
if (base < len) {
((base));
}
}
return ();
}
public void addBatch() throws SQLException {
();
}
public void clearParameters() throws SQLException {
();
}
public boolean execute() throws SQLException {
return ();
}
public ResultSet executeQuery() throws SQLException {
return ();
}
public int executeUpdate() throws SQLException {
return ();
}
public ResultSetMetaData getMetaData() throws SQLException {
return ();
}
public ParameterMetaData getParameterMetaData() throws SQLException {
return ();
}
public void setArray(int i, Array x) throws SQLException {
(i, x);
saveQueryParamValue(i, x);
}
public void setAsciiStream(int parameterIndex, InputStream x, int length)
throws SQLException {
(parameterIndex, x, length);
saveQueryParamValue(parameterIndex, x);
}
public void setBigDecimal(int parameterIndex, BigDecimal x)
throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
(parameterIndex, x, length);
saveQueryParamValue(parameterIndex, x);
}
public void setBlob(int i, Blob x) throws SQLException {
(i, x);
saveQueryParamValue(i, x);
}
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Boolean(x));
}
public void setByte(int parameterIndex, byte x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Byte(x));
}
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setCharacterStream(int parameterIndex, Reader reader, int length)
throws SQLException {
(parameterIndex, reader, length);
saveQueryParamValue(parameterIndex, reader);
}
public void setClob(int i, Clob x) throws SQLException {
(i, x);
saveQueryParamValue(i, x);
}
public void setDate(int parameterIndex, Date x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setDate(int parameterIndex, Date x, Calendar cal)
throws SQLException {
(parameterIndex, x, cal);
saveQueryParamValue(parameterIndex, x);
}
public void setDouble(int parameterIndex, double x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Double(x));
}
public void setFloat(int parameterIndex, float x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Float(x));
}
public void setInt(int parameterIndex, int x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Integer(x));
}
public void setLong(int parameterIndex, long x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Long(x));
}
public void setNull(int parameterIndex, int sqlType) throws SQLException {
(parameterIndex, sqlType);
saveQueryParamValue(parameterIndex, new Integer(sqlType));
}
public void setNull(int paramIndex, int sqlType, String typeName)
throws SQLException {
(paramIndex, sqlType, typeName);
saveQueryParamValue(paramIndex, new Integer(sqlType));
}
public void setObject(int parameterIndex, Object x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setObject(int parameterIndex, Object x, int targetSqlType)
throws SQLException {
(parameterIndex, x, targetSqlType);
saveQueryParamValue(parameterIndex, x);
}
public void setObject(int parameterIndex, Object x, int targetSqlType,
int scale) throws SQLException {
(parameterIndex, x, targetSqlType, scale);
saveQueryParamValue(parameterIndex, x);
}
public void setRef(int i, Ref x) throws SQLException {
(i, x);
saveQueryParamValue(i, x);
}
public void setShort(int parameterIndex, short x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Short(x));
}
public void setString(int parameterIndex, String x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setTime(int parameterIndex, Time x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setTime(int parameterIndex, Time x, Calendar cal)
throws SQLException {
(parameterIndex, x, cal);
saveQueryParamValue(parameterIndex, x);
}
public void setTimestamp(int parameterIndex, Timestamp x)
throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
throws SQLException {
(parameterIndex, x, cal);
saveQueryParamValue(parameterIndex, x);
}
public void setURL(int parameterIndex, URL x) throws SQLException {
(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}
public void setUnicodeStream(int parameterIndex, InputStream x, int length)
throws SQLException {
(parameterIndex, x, length);
saveQueryParamValue(parameterIndex, x);
}
public void addBatch(String sql) throws SQLException {
(sql);
}
public void cancel() throws SQLException {
();
}
public void clearBatch() throws SQLException {
();
}
public void clearWarnings() throws SQLException {
();
}
public void close() throws SQLException {
();
}
public boolean execute(String sql) throws SQLException {
return (sql);
}
public boolean execute(String sql, int autoGeneratedKeys)
throws SQLException {
return (sql, autoGeneratedKeys);
}
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return (sql, columnIndexes);
}
public boolean execute(String sql, String[] columnNames)
throws SQLException {
return (sql, columnNames);
}
public int[] executeBatch() throws SQLException {
return ();
}
public ResultSet executeQuery(String sql) throws SQLException {
return (sql);
}
public int executeUpdate(String sql) throws SQLException {
return (sql);
}
public int executeUpdate(String sql, int autoGeneratedKeys)
throws SQLException {
return (sql, autoGeneratedKeys);
}
public int executeUpdate(String sql, int[] columnIndexes)
throws SQLException {
return (sql, columnIndexes);
}
public int executeUpdate(String sql, String[] columnNames)
throws SQLException {
return (sql, columnNames);
}
public Connection getConnection() throws SQLException {
return ();
}
public int getFetchDirection() throws SQLException {
return ();
}
public int getFetchSize() throws SQLException {
return ();
}
public ResultSet getGeneratedKeys() throws SQLException {
return ();
}
public int getMaxFieldSize() throws SQLException {
return ();
}
public int getMaxRows() throws SQLException {
return ();
}
public boolean getMoreResults() throws SQLException {
return ();
}
public boolean getMoreResults(int current) throws SQLException {
return (current);
}
public int getQueryTimeout() throws SQLException {
return ();
}
public ResultSet getResultSet() throws SQLException {
return ();
}
public int getResultSetConcurrency() throws SQLException {
return ();
}
public int getResultSetHoldability() throws SQLException {
return ();
}
public int getResultSetType() throws SQLException {
return ();
}
public int getUpdateCount() throws SQLException {
return ();
}
public SQLWarning getWarnings() throws SQLException {
return ();
}
public void setCursorName(String name) throws SQLException {
(name);
}
public void setEscapeProcessing(boolean enable) throws SQLException {
(enable);
}
public void setFetchDirection(int direction) throws SQLException {
(direction);
}
public void setFetchSize(int rows) throws SQLException {
(rows);
}
public void setMaxFieldSize(int max) throws SQLException {
(max);
}
public void setMaxRows(int max) throws SQLException {
(max);
}
public void setQueryTimeout(int seconds) throws SQLException {
(seconds);
}
public void setAsciiStream(int parameterIndex, InputStream x)
throws SQLException {
}
public void setAsciiStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
public void setBinaryStream(int parameterIndex, InputStream x)
throws SQLException {
}
public void setBinaryStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
public void setBlob(int parameterIndex, InputStream inputStream)
throws SQLException {
}
public void setBlob(int parameterIndex, InputStream inputStream, long length)
throws SQLException {
}
public void setCharacterStream(int parameterIndex, Reader reader)
throws SQLException {
}
public void setCharacterStream(int parameterIndex, Reader reader,
long length) throws SQLException {
}
public void setClob(int parameterIndex, Reader reader) throws SQLException {
}
public void setClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
public void setNCharacterStream(int parameterIndex, Reader value)
throws SQLException {
}
public void setNCharacterStream(int parameterIndex, Reader value,
long length) throws SQLException {
}
public void setNClob(int parameterIndex, NClob value) throws SQLException {
}
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
}
public void setNClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
public void setNString(int parameterIndex, String value)
throws SQLException {
}
public void setRowId(int parameterIndex, RowId x) throws SQLException {
}
public void setSQLXML(int parameterIndex, SQLXML xmlObject)
throws SQLException {
}
public boolean isClosed() throws SQLException {
return false;
}
public boolean isPoolable() throws SQLException {
return false;
}
public void setPoolable(boolean poolable) throws SQLException {
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public void closeOnCompletion() throws SQLException {
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
return false;
}
}