兴趣使然之封装Spring的JdbcTemplate

编程

使用过几个ORM框架,都感觉并不合适我使用,SpringData与Hibernate虽然强大,但是太多功能我日常工作或者学习上并不需要,且在调优问题上有一定的阻碍,而MyBatis又需要把sql写到xml文件或者注解里面,而我更喜欢sql直接写到代码里面用Java代码写逻辑控制sql语句。就想着封装一个简单方便适合自己日常学习使用的ORM框架。相比直接封装原生的JDBC,Spring提供的JdbcTemplate工具类已经提供了一条捷径,所以这次简单封装一下JdbcTemplate,使平时学习能更加顺手

先展示一下使用(下面用的都是main函数测试,因为不想引入太多依赖导致后面我学习使用时还要再删除):

下面是上面调用的testSave()函数

下面是上面调用的save()函数

save()执行完以后数据库多了条数据并且把执行的sql打印出来了(打印用的是System.out.println打印,上图截完才加上的),我的数据库id是自增的,所以插入的时候没有设置值,又把我刚封装好时测试的数据删除了,所以就从11开始

下面内容仅用于记录封装,不做太详细说明

一,映射注解

ORM框架都是由对象与数据映射关系组成,为了标明某个对象映射某个表的,对象的字段对应表的哪个字段,所以先写了2个注解 @Column 与 @Table

@Table仅用于注明是映射哪个表

@Column用于注明映射哪个字段,及是否为主键

/**

* 用于标识类对应数的表

*/

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Table {

String value() default "";

}

/**

* 用于标识字段对应数的列名

*/

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Column {

String value() default "";

boolean isKey() default false;

}

下面是使用方式,getXxx()和setXxx()函数就不复制出来了,另外最后一个test字段没使用@Column则不进行映射关系,其他用到注解的地方都说明了对应的是哪个表,哪个字段,如果是联合主键则多字段使用 isKey=true

@Table("sys_user")

public class User implements Serializable {

public final static String TABLE_USER="sys_user";

@Column(value = "id",isKey = true)

private Long id;

@Column("user_name")

private String userName;

@Column("password")

private String password;

@Column("nick_name")

private String nickName;

@Column("gender")

private Integer gender;

@Column("mobile")

private String mobile;

@Column("email")

private String email;

private String test;

二,根据映射对象生成DML语句

编写了一个DaoUtils的泛型类,这样就可以根据各种对象动态生成sql

里面有13个函数(一个main方法,前期测试使用),1个属性

table属性:主要用于存储通过analysisTable(T t)函数反射获取到的表信息与字段信息

/**

* 存表信息字段信息

*/

private Map<String,Object> table;

analysisTable(T t):通过反射获取到的表信息和字段信息

下面是table属性储存的属性

tableName:存储表名

tableColumns:储存表字段信息

keyColumns:储存主键信息

/**

* 根据取表信息,表名,字段名,字段

* @return

* @throws Exception

*/

private Map<String,Object> analysisTable(T t) throws DtoAnalysisTableException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

Class<?> clazz = t.getClass();

// 象所有的信息集合,表,字段,字段

Map<String,Object> result = new HashMap<String,Object>();

// 存字段信息

Map<String,Object> fieldMap = new TreeMap<String, Object>();

// 存主信息

Map<String,Object> keyMap = new TreeMap<String, Object>();

// 名,Table注解

Table table = clazz.getAnnotation(Table.class);

if(table == null) {

throw new DtoAnalysisTableException("Object not has Table of Annotation");

}

// 取所有字段名

Field[] fields = clazz.getDeclaredFields();

// 字段字段的信息

for (Field field: fields) {

// 取字段映射信息

Column annotation = field.getAnnotation(Column.class);

// 存在映射信息候才

if(annotation != null){

// getXxx()

Method getMethod = clazz.getDeclaredMethod(MethodUtils.getMethod(field.getName()));

// 根据getXxx数获

Object invoke = getMethod.invoke(t);

// 该数字段对应

fieldMap.put(annotation.value(), invoke);

// 是否主,如果是到主信息

if(annotation.isKey()){

keyMap.put(annotation.value(),invoke);

}

}

}

// 添加表名

result.put("tableName", table.value());

// 添加字段信息

result.put("tableColumns", fieldMap);

// 添加主字段信息

result.put("keyColumns",keyMap);

return result;

}

上面用到的自定义类 MethodUtils 与自定义异常 DtoAnalysisTableException

MethodUtils 主要提供了根据字段名获取getXxx()和setXxx()的函数

/**

* 入一字段名,字段的

* getXxx()

* @param fieldName

* @return

*/

public static String getMethod(String fieldName){

if(fieldName == null || fieldName.length() < 1){

return "";

}

return "get"+fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);

}

/**

* 入一字段名,字段的

* setXxx()

* @param fieldName

* @return

*/

public static String setMethod(String fieldName){

if(fieldName == null || fieldName.length() < 1){

return "";

}

return "set"+fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);

}

DtoAnalysisTableException这是该类没有表映射关系的异常

/**

* 映射

*/

public class DtoAnalysisTableException extends Exception{

public DtoAnalysisTableException(String message) {

super(message);

}

}

接着说回DaoUtils的函数

getTable(T t) 是获取 table的函数,避免同一DaoUtils对象多次调用analysisTable(T t)浪费资源

/**

* 用于映射的表信息

* @param t

* @return

*/

private Map<String, Object> getTable(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

if(table == null) {

table = analysisTable(t);

}

return table;

}

insertCols() 与 updateCols()则是获取的 sql 的部分语句 例如下面图片红圈出:

/**

* 根据getTable()返回的集合 tableColumns 取持久化象的性名字符串

* @param colMap

* @return

*/

private String insertCols(Map<String,Object> colMap){

StringBuffer insertCols = new StringBuffer("");

// key(表字段名)

Set<String> keySet = colMap.keySet();

// key对应接部分sql句,例如:" fieldName1,fieldName2,fieldName3 "

for (String col : keySet) {

if(insertCols.length() > 0){

insertCols.append(",");

}

insertCols.append(col);

}

return insertCols.toString();

}

/**

* 根据getTable()返回的集合 tableColumns 取持久化象的性名字符串

* @param colMap

* @return

*/

private String updateCols(Map<String,Object> colMap){

StringBuffer insertCols = new StringBuffer("");

// key(表字段名)

Set<String> keySet = colMap.keySet();

// key对应接部分sql句,例如:"fieldName1=?,fieldName2=?,fieldName3=? "

for (String col : keySet) {

if(insertCols.length() > 0){

insertCols.append(",");

}

insertCols.append(col).append("=?");

}

return insertCols.toString();

}

下面的 insertSql(T t) insertBatchSql(T t,int size)updateSql(T t)deleteSql(T t) 则是获取插入、批量插入、根据主键修改、根据主键删除的SQL

/**

* 根据表信息,insert sql

* @return

*/

public String insertSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 反射

Map<String, Object> tableByDto = getTable(t);

// 取表名

String tableName = tableByDto.get("tableName")+"";

// 取表字段

Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");

// sql

StringBuffer sql = new StringBuffer(" INSERT INTO ");

// "" 这样并拼

sql.append(tableName).append(" (").append(insertCols(colMap)).append(") ");

// "?,?,?,?,?" 这样并拼

sql.append(" VALUES ").append(" (").append(insertParam(colMap)).append(") ");

return sql.toString();

}

/**

* 根据表信息,批量insert sql

* @param size 批量的

* @return

*/

public String insertBatchSql(T t,int size) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 反射

Map<String, Object> tableByDto = getTable(t);

// 取表字段

Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");

StringBuffer sql = new StringBuffer(insertSql(t));

// "?,?,?,?,?" 这样

String insertParam = insertParam(colMap);

// sql

for (int i = 0 ; i < size - 1; i++){

sql.append(" , (").append(insertParam).append(") ");

}

return sql.toString();

}

/**

* 根据表信息,update sql

* @return

*/

public String updateSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 反射

Map<String, Object> tableByDto = getTable(t);

// 取表字段

Map<String, Object> colMap = (Map<String, Object>) tableByDto.get("tableColumns");

// 取表主

Map<String, Object> keyMap = (Map<String, Object>) tableByDto.get("keyColumns");

//

String tableName = tableByDto.get("tableName")+"";

Set<String> keys = keyMap.keySet();

// UPDATE SQL

StringBuffer sql = new StringBuffer(" UPDATE ");

sql.append(tableName).append(" set ").append(updateCols(colMap)).append(" ");

sql.append(" where ");

int i = 0;

for (String key : keys) {

sql.append(key).append("=? ").append((i++ < keys.size()-1) ? " and " : "");

}

return sql.toString();

}

/**

* 根据表信息,取根据主delete sql

* @return

*/

public String deleteSql(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 反射

Map<String, Object> tableByDto = getTable(t);

// 取表名

String tableName = tableByDto.get("tableName")+"";

// 取主

Map<String, Object> keyMap = (Map<String, Object>) tableByDto.get("keyColumns");

Set<String> keys = keyMap.keySet();

// sql

StringBuffer sql = new StringBuffer(" DELETE from ");

sql.append(tableName).append(" WHERE ");

int i = 0;

for (String key : keys) {

sql.append(key).append("=? ").append((i++ < keys.size()-1) ? " and " : "");

}

return sql.toString();

}

上面有使用到 insertParam() 函数 主要是用于获取插入语句的 “?,?,?,?” 这样的内容

/**

* 参数字符串 格式:?,?,?,?

* @param colMap

* @return

*/

private String insertParam(Map<String,Object> colMap){

StringBuffer param = new StringBuffer("");

for (int i = 0;i < colMap.size(); i++){

param.append("?");

if(i < colMap.size()-1){

param.append(",");

}

}

return param.toString();

}

再接下来是获取使JdbcTemplate接口时候需要使用的参数值。insertValues(T t)insertBatchValues(List<T> list)updateValues(T t)deleteValues(T t) 对应的分别是获取插入、批量插入、根据主键修改和根据主键删除的函数

/**

* 根据getTable()返回的集合 tableColumns 象的

* @param t

* @return

*/

public Object[] insertValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 取表信息

Map<String, Object> table = getTable(t);

// 取表字段

Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");

Object[] values = new Object[colMap.size()];

// 根据字段名

Set<String> keySet = colMap.keySet();

int i = 0;

for (String col : keySet) {

values[i++] = colMap.get(col);

}

return values;

}

/**

* 根据getTable()返回的集合 tableColumns 象的

* @return

*/

public Object[] insertBatchValues(List<T> list) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 用于存所有参数

List<Object> values = new ArrayList<Object>();

// 参数

for (int i = 0;i < list.size(); i++) {

// insertValues该对象的参数值

Object[] objects = insertValues(list.get(i));

// 参数添加到列表

values.addAll(Arrays.asList(objects));

}

return values.toArray();

}

/**

* 根据getTable()返回的集合 tableColumns 象的

* @param t

* @return

*/

public Object[] updateValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 取表信息

Map<String, Object> table = getTable(t);

// 取所有表字段

Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");

// 取主字段

Map<String, Object> keyMap = (Map<String, Object>) table.get("keyColumns");

List<Object> values = new ArrayList<Object>();

// 取所有字段

Set<String> keySet = colMap.keySet();

// 根据key对应

for (String col : keySet) {

values.add(colMap.get(col));

}

// 取主字段

Set<String> keys = keyMap.keySet();

// 根据key对应

for (String key : keys) {

values.add(colMap.get(key));

}

return values.toArray();

}

/**

* 根据getTable()返回的集合 tableColumns 象的

* @param t

* @return

*/

public Object[] deleteValues(T t) throws NoSuchMethodException, IllegalAccessException, DtoAnalysisTableException, InvocationTargetException {

// 取表信息

Map<String, Object> table = getTable(t);

// 取字段信息

Map<String, Object> colMap = (Map<String, Object>) table.get("tableColumns");

// 取主信息

Map<String, Object> keyMap = (Map<String, Object>) table.get("keyColumns");

// key(表性)

Set<String> keys = keyMap.keySet();

List<Object> values = new ArrayList<Object>();

// 根据key对应

for (String key : keys) {

values.add(colMap.get(key));

}

return values.toArray();

}

对于这个类获取SQL的测试,使用main()调用

// 测试使用

public static void main(String[] args) {

try{

User user = new User();

user.setUserName("11111");

user.setPassword("fdsfds");

user.setMobile("137");

user.setEmail("email");

// 添加sql

String insertSql = new DaoUtils<User>().insertSql(user);

System.out.println(insertSql);

// 修改sql

String updateSql = new DaoUtils<User>().updateSql(user);

System.out.println(updateSql);

// 修改sql

String deleteSql = new DaoUtils<User>().deleteSql(user);

System.out.println(deleteSql);

// 批量添加sql

String insertBatchSql = new DaoUtils<User>().insertBatchSql(user,3);

System.out.println(insertBatchSql);

} catch (Exception e){

e.printStackTrace();

}

}

打印结果:

下面就是封装好的 BaseDao

public class BaseDao<T> {

protected JdbcTemplate jdbcTemplate;

/**

* 入函

* @param t

*/

public void save(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {

// Util

DaoUtils<T> du = new DaoUtils<T>();

// sql

String s = du.insertSql(t);

// sql参数

Object[] params = du.insertValues(t);

// JdbcTemplate

jdbcTemplate.update(s,params);

}

/**

* 修改函

* @param t

*/

public void update(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {

// Util

DaoUtils<T> du = new DaoUtils<T>();

// sql

String s = du.updateSql(t);

// sql参数

Object[] params = du.updateValues(t);

// JdbcTemplate

jdbcTemplate.update(s,params);

}

/**

* 除函

* @param t

*/

public void delete(T t) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {

// Util

DaoUtils<T> du = new DaoUtils<T>();

// sql

String s = du.deleteSql(t);

// sql参数

Object[] params = du.deleteValues(t);

// JdbcTemplate

jdbcTemplate.update(s,params);

}

/**

* 批量入函

* @param list

*/

public void batchSave(List<T> list) throws InvocationTargetException, NoSuchMethodException, DtoAnalysisTableException, IllegalAccessException {

// Util

DaoUtils<T> du = new DaoUtils<T>();

// sql

String s = du.insertBatchSql(list.get(0), list.size());

// sql参数

Object[] params = du.insertBatchValues(list);

// JdbcTemplate

jdbcTemplate.update(s,params);

}

// 测试

public JdbcTemplate getJdbcTemplate() {

return jdbcTemplate;

}

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

}

当然,最开始只是因为测试,所以直接使用的BaseDao,实际写一个UserDao再继承BaseDao使用更方便,这样可以使用增删改的工具函数,也能定义属于User类自己的sql例如:

public class UserDao extends BaseDao<User> {

public List<User> list(){

return jdbcTemplate.query(" select * from " + User.TABLE_USER , new Object[]{},new LocalRowMapper<User>(User.class));

}

}

上面BaseDao的增删改工具函数就不再进行测试了

三,下面开始说说查询

JdbcTemplate 原生的query()是不支持类字段和表字段映射规则不一样的查询操作,例如:数据库字段 user_name 与 类属性 userName 这2个可以映射到,query时会根据下划线"_"的位置进行驼峰命名,但是如果遇到 user_name 与 userName001 这样的字段,则不能通过映射,下面测试一下

这是表的内容

先测试用JdbcTemplate提供的 BeanPropertyRowMapper 执行

查询list函数

测试函数

main()

看看执行内容:证明如果是user_name和userName能够映射

再把我们上面的User类代码的userName改成userName1,

main()执行结果,可以看到userName1的字段是null,并没有映射到:

下面再改成我自己重写RowMapper后的 LocalRowMapper,其他代码不变,把BeanPropertyRowMapper 改成 LocalRowMapper

执行结果,换成了LocalRowMapper之后userName1与user_name也能映射到,userName与user_name更不用说了:

userName1

userName

下面看看 LocalRowMapper 类

/**

* RowMapper,使使用@Table @Column

* @param <T>

*/

public class LocalRowMapper<T> implements RowMapper<T>{

private Class<?> clazz;

// 保存字段与数字段的映射

private Map<String,String> fieldMap;

// 保存字段setXxx()参数类

private Map<String,Class> fieldTypeMap;

public LocalRowMapper(Class<?> clazz){

try {

// calss

this.clazz = clazz;

// 初始化 fieldMap

this.fieldMap = new HashMap<String, String>();

// 初始化 fieldTypeMap

this.fieldTypeMap = new HashMap<String, Class>();

this.analysisTable();

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* RowMapper mapRow()

* @param rs

* @param arg

* @return

*/

public T mapRow(ResultSet rs, int arg) {

T t= null;

try {

// 根据class 例化

t = (T)clazz.newInstance();

// 与数映射的字段

Set<String> keySet = this.fieldMap.keySet();

for (String key: keySet) {

// setXxx()

Method setMethod = clazz.getDeclaredMethod(MethodUtils.setMethod(key),this.fieldTypeMap.get(key));

// 根据字段名称获ResultSet返回字段所在下

// 标获取到于字段的

// setXxx() 值设置到

setMethod.invoke(t,rs.getObject(rs.findColumn(this.fieldMap.get(key))));

}

} catch (Exception e) {

e.printStackTrace();

}

return t;

}

/**

* 取字段映射信息函

* @throws DtoAnalysisTableException

* @throws NoSuchMethodException

* @throws InvocationTargetException

* @throws IllegalAccessException

*/

private void analysisTable() throws DtoAnalysisTableException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

Class<?> clazz = this.clazz;

// 取所有字段名

Field[] fields = clazz.getDeclaredFields();

// 字段字段的信息

for (Field field: fields) {

Column annotation = field.getAnnotation(Column.class);

if(annotation != null){

// 保存字段与数字段

this.fieldMap.put(field.getName(), annotation.value());

// 保存字段setXxx()参数类

this.fieldTypeMap.put(field.getName(),field.getType());

}

}

}

}

以上就是对这次 JdbcTemplate装的所有内容了,下面是源码链接(OSCHINA不能上传附件只能放云盘了)

https://pan.baidu.com/s/1e0DsZKP_D9dWVcZ4TRayHQ  

提取码:awip

 

 

以上是 兴趣使然之封装Spring的JdbcTemplate 的全部内容, 来源链接: utcz.com/z/514527.html

回到顶部