`
剑锋凛冽
  • 浏览: 75530 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

JDBCTemplate+JavaPOJO实现通用DAO

阅读更多

最近在公司实习过程中,TL提出一个需求,要求在不使用Hibernate的情况下实现一个比较通用的DAO框架,使用JDBCTemplate作为数据库sql语句的执行工具。在参考了CloudStack 3.0.2的相关源代码后,我自己实现了一个简化版的DAO框架。结果后来,TL又说改用Python开发,遗憾地把这些东西留作纪念吧。

 

简单的类图参见连接http://pan.baidu.com/share/link?shareid=115118&uk=3592520259

 

环境为MyEclipse8.5+Spring2.5,使用jar为asm-3.3.1、cglib-2.2、mysql-connector

1,编程思想

本质上是将某些通用的API,如最基础的CRUD直接通过泛型类来实现。唯一的比较难处理的就是Update时,哪些属性需要更新,这可以拦截通过CGLIB类库实现对setter方法的拦截并记录被改变的属性。

 

2,代码

核心类BaseDaoImpl

package DataBaseDemo.daoimpl;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import net.sf.cglib.proxy.Enhancer;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import DataBaseDemo.interceptor.UpdateFactory;
import DataBaseDemo.util.DBUtils;
import DataBaseDemo.util.ModelRowMapper;

import com.mysql.jdbc.Statement;

public class BaseDaoImpl<T> {
    Logger logger=Logger.getLogger(BaseDaoImpl.class);
    //POJO类的实际类型
	Class<T> entityType;
	//简单地将POJO类名映射成数据库表名
	String table;
	public static JdbcTemplate jdbcTemplate;
    public static PlatformTransactionManager transactionManager;
    public static DefaultTransactionDefinition  transactionDef;
	@SuppressWarnings("unchecked")
	BaseDaoImpl() {
		DataSource datasource=DBUtils.configureDatasource();
		jdbcTemplate = new JdbcTemplate(datasource);
		transactionManager=new DataSourceTransactionManager(datasource);
		transactionDef=new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED);
		Type t = getClass().getGenericSuperclass();
		// 使用该语法后,BaseDaoImpl无法正常使用,只能通过子类调用它
		// 用于获取实际输入的Model类型
		if (t instanceof ParameterizedType) {
			entityType = (Class<T>) ((ParameterizedType) t)
					.getActualTypeArguments()[0];
		} else if (((Class<?>) t).getGenericSuperclass() instanceof ParameterizedType) {
			entityType = (Class<T>) ((ParameterizedType) ((Class<?>) t)
					.getGenericSuperclass()).getActualTypeArguments()[0];
		} else {
			entityType = (Class<T>) ((ParameterizedType) ((Class<?>) ((Class<?>) t)
					.getGenericSuperclass()).getGenericSuperclass())
					.getActualTypeArguments()[0];
		}
		this.table = DBUtils.getTable(entityType);
	}

	@SuppressWarnings("unchecked")
	public List<T> queryAll() {
		String sql = "select * from " + table;
		List<T> list = jdbcTemplate.query(sql, new ModelRowMapper(entityType));
		return list;
	}

	/**
	 * 根据ID,查询一条记录并用实体包装
	 * 
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T load(int id) {
		String sql = "select * from " + table + " where id=" + id;
		List<T> list = jdbcTemplate.query(sql, new ModelRowMapper(entityType));
		return list.size() == 0 ? null : list.get(0);
	}

	/**
	 * 根据ID,删除指定表里的记录
	 * 
	 * @param id
	 */
	public void delete(int id) {
		String sql = "delete from " + table + " where id=" + id;
		jdbcTemplate.execute(sql);
	}

	/**
	 * 根据ID更新实体数据到数据库
	 * 
	 * @param entity
	 * @param id
	 */
	@SuppressWarnings("unchecked")
	public void update(T entity, int id) {
		assert Enhancer.isEnhanced(entity.getClass()) : "没有被拦截器监控到更新数据";
		StringBuilder sql = new StringBuilder();
		sql.append("update " + table + " set ");
		System.out.println(entity.hashCode());
		HashMap<String, Object> map = UpdateFactory.getChanges(entity.hashCode());
		List<String> keys = new ArrayList<String>();
		List<Object> values = new ArrayList<Object>();
		Iterator iter = map.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry entry = (Map.Entry) iter.next();
			String key = (String) entry.getKey();
			Object val = entry.getValue();
			keys.add(key);
			values.add(val);
		}
		for (int i = 0; i < keys.size(); i++) {
			if (i == keys.size() - 1) {
				sql.append(keys.get(i) + "=? ");
			} else {
				sql.append(keys.get(i) + "=?,");
			}
		}
		sql.append("where id=?");
		logger.info("更新语句:"+sql.toString());
		values.add(id);
		jdbcTemplate.update(sql.toString(), setParams(values.toArray()));
	}

	/**
	 * 插入实体,并返回数据库自增生成的ID
	 * 
	 * @param entity
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public int insert(T entity) {
		final StringBuilder sql = new StringBuilder();
		
		sql.append("insert into " + table + "(");
		HashMap<String, Object> map = getChangesForInsert(entity);
		List<String> columns = new ArrayList<String>();
		final List<Object> values = new ArrayList<Object>();
		Iterator iter = map.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry entry = (Map.Entry) iter.next();
			String key = (String) entry.getKey();
			Object val = entry.getValue();
			columns.add(key);
			values.add(val);
		}
		for (int i = 0; i < columns.size(); i++) {
			if (i == columns.size() - 1) {
				sql.append(columns.get(i) + ") values(");
			} else {
				sql.append(columns.get(i) + ",");
			}
		}

		for (int i = 0; i < values.size(); i++) {
			if (i == values.size() - 1) {
				sql.append("?)");
			} else {
				sql.append("?,");
			}
		}
		logger.info("插入语句:"+sql.toString());
		KeyHolder key = new GeneratedKeyHolder();
		final String insertSql=sql.toString();
		jdbcTemplate.update(new PreparedStatementCreator() {

			@Override
			public PreparedStatement createPreparedStatement(Connection con)
					throws SQLException {
				// TODO Auto-generated method stub
				//必须设置Statement.RETURN_GENERATED_KEYS才能进行返回ID
				PreparedStatement ps = jdbcTemplate.getDataSource()
						.getConnection().prepareStatement(insertSql,Statement.RETURN_GENERATED_KEYS);
				for (int i = 0; i < values.size(); i++) {
					ps.setObject(i + 1, values.get(i));
				}
				return ps;
			}
		}, key);
		return key.getKey().intValue();
	}

	/**
	 * 插入实体并返回被插入的实体
	 * 
	 * @param entity
	 * @return
	 */
	public T persist(final T entity) {
		int id=insert(entity);
		//直接通过connection进行提交同样无法成功
//		transaction.commit();
		logger.info("数据库返回的自增ID为:"+id);
		T persisted=load(id);
		return persisted;
	}
	/**
	 * 
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> query(SearchCriteria sc){
		String where = sc.generateWhereClause();
		StringBuilder sb = new StringBuilder("select * from "+sc.getTable());
		sb.append(where);
		logger.info("查询语句"+sb.toString());
		logger.info("查询参数"+Arrays.toString(sc.generateParams()));
		List<T> list = jdbcTemplate.query(sb.toString(), setParams(sc.generateParams()),new ModelRowMapper(entityType));
		return list;
	}
	/**
	 * 返回DAO对应的数据库表的总记录数
	 * @return
	 */
	public int getTotalCount(){
		String sql="select count(*) from "+table;
		return jdbcTemplate.queryForInt(sql);
	}
	/**
	 * 以pagesize大小的页,返回第page页的数据
	 * @param page
	 * @param pagesize
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> getPage(int page,int pagesize){
		if(page<0||pagesize<0){
			throw new IllegalArgumentException("页码或页大小参数不合法");
		}
		String sql="select * from "+table+" limit "+page*pagesize+","+(page+1)*pagesize;
		return jdbcTemplate.query(sql, new ModelRowMapper<T>(entityType));
	}
	/**
	 * 直接执行sql查询语句,param作为参数数组
	 * @param sql
	 * @param params
	 * 返回查询到的结果列表
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> executeRawSql(String sql,Object[] params){
		return jdbcTemplate.query(sql, setParams(params)
				, new ModelRowMapper<T>(entityType));
	}

	/**
	 * 设置查询用的参数列表
	 * @param params
	 * @return
	 */
	protected PreparedStatementSetter setParams(final Object[] params) {
		return new PreparedStatementSetter() {

			@Override
			public void setValues(PreparedStatement ps) throws SQLException {
				// TODO Auto-generated method stub
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]);
				}
			}
		};
	}
	/**
	 * 返回待插入实体上的所有非空属性值及属性名的Map
	 * @param entity
	 * @return
	 */
	protected HashMap<String, Object> getChangesForInsert(T entity){
		Field[] fields = entityType.getDeclaredFields();
		
		HashMap<String, Object> insertValues = new HashMap<String, Object>();
		try {
			for (Field field : fields) {
				field.setAccessible(true);
				//跳过id字段
				if("id".equalsIgnoreCase(field.getName()))
					continue;
				Object value = field.get(entity);
				if (value == null)
					continue;
				insertValues.put(field.getName(), value);
				
			}
			return insertValues;
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}
 

子类Dao实例:UserDaoImpl

package DataBaseDemo.daoimpl;

import java.util.List;

import DataBaseDemo.dao.UserDao;
import DataBaseDemo.model.UserVO;

public class UserDaoImpl extends BaseDaoImpl<UserVO> implements UserDao {

	@Override
	public UserVO queryUser() {
		// TODO Auto-generated method stub
		UserVO user=new UserVO();
		return user;
	}
	//自定义的高级查询包装
	public List<UserVO> listUsers(){
		return queryAll();
	}

}
 

核心拦截工厂对Model被修改的属性进行记录并通过CGLIB的接口进行拦截

package DataBaseDemo.interceptor;

import java.util.HashMap;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
/**
 * 使用UpdateFactory存放对象被改变的属性及其值
 * 以对象的hashCode为key,值为被改变的HashMap
 * @author Administrator
 *
 */
public class UpdateFactory {


	public static HashMap<Integer,HashMap<String,Object>> changes;
	private static Enhancer enhancer;
	//以字典的方式记录每个对象的改变属性值
	static{
		changes=new HashMap<Integer, HashMap<String,Object>>();
	}
	
	/**
	 * 根据对象的hashCode存储对象被改变的属性值
	 * @param hash
	 * @param key
	 * @param value
	 */
	public static void addChange(Integer hash,String key,Object value){
		HashMap<String, Object> orginal=changes.get(hash);
		if(orginal==null){
			orginal=new HashMap<String, Object>();
			orginal.put(key, value);
		}else{
			orginal.put(key, value);
		}
		
		changes.put(hash, orginal);
	}
	
	/**
	 * 以对象的hashCode取出对象的所有变更
	 * @param hash
	 * @return
	 */
	public static HashMap<String, Object> getChanges(Integer hash){
		return changes.get(hash);
	}
	
	
	// 通过工厂生成对象,并产生拦截器,拦截set方法生成被改变的值Map
	/**
	 * 根据对象class生成对应实例,并使它的修改能够被CGLIB拦截
	 */
	public static Object createVO(Class<?> clazz) {
		enhancer = new Enhancer();
		enhancer.setSuperclass(clazz);
		Callback[] callbacks;
		callbacks = new Callback[] { NoOp.INSTANCE, new UpdateInterceptor() };
		enhancer.setCallbacks(callbacks);
		enhancer.setCallbackFilter(new SetFilter());
		return enhancer.create();
	}
}
 package DataBaseDemo.util;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;

import org.springframework.jdbc.core.RowMapper;

import DataBaseDemo.interceptor.UpdateFactory;

/**
 * 使用包cglib和asm来创建对某一对象setters方法的拦截器
 * 
 * @author Administrator
 * 
 */
public class ModelRowMapper<T> implements RowMapper {

	/**
	 * @param args
	 */
	Class<?> clazz;

	public ModelRowMapper(Class<?> clazz) {
		this.clazz = clazz;
	}

	// RowMapper中直接通过field给字段设值,避免干扰set拦截器的使用
	public static Object setValues(HashMap<String, Object> map, Object entity) {
		Field[] fields = entity.getClass().getDeclaredFields();
		try {
			for (Field field : fields) {
				Object value = map.get(field.getName());
				if (value != null) {
					field.setAccessible(true);
					field.set(entity, value);
				}
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return entity;
	}

	public void setValues(ResultSet rs, Object entity) {
		Field[] fields = clazz.getDeclaredFields();
		try {
			for (Field field : fields) {
				Object value = rs.getObject(field.getName());
				field.setAccessible(true);
				field.set(entity, value);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public T mapRow(ResultSet rs, int rowNum) throws SQLException {
		//通过更新工厂的静态方法创建类实例,使它被CGLIB监控
		T entity = (T) UpdateFactory.createVO(clazz);
		setValues(rs, entity);
		return entity;
	}

}

 3,缺点

--目前的查询非常简单,需要进行优化

--无法支持事务管理,原因不明,进一步研究中

所有源码参照CloudStack3.0.2的相关代码编写

分享到:
评论

相关推荐

    Oracle + jdbcTemplate + Spring + Java + Flex 实现分页

    ### Oracle + jdbcTemplate + Spring + Java + Flex 实现分页 #### 一、Oracle存储过程分页 在Oracle数据库中,为了实现高效的分页查询,通常会采用存储过程的方式来完成。这种方式能够有效地减少网络传输的数据量...

    Oracle + jdbcTemplate + Spring + Java + Flex 实现分页.docx

    总结起来,实现Oracle + jdbcTemplate + Spring + Java + Flex的分页查询,主要涉及以下步骤: 1. 在Oracle中创建存储过程,处理分页逻辑和计数。 2. 使用Spring的jdbcTemplate调用存储过程,处理输入输出参数。 3. ...

    springmvc+jdbctemplate+mysql(采用注解方式)

    总的来说,"springmvc+jdbctemplate+mysql(采用注解方式)"的组合为开发中小型项目提供了高效、灵活的基础。通过注解方式,开发者可以快速搭建系统,专注于业务逻辑,而无需过多关注底层配置。这个技术栈的使用能够...

    基于java+Spring+SpringMVC+JDBCTemplate+JSP开发的博客论坛系统+源码+开发文档+视频演示

    基于java+Spring+SpringMVC+JDBCTemplate+JSP开发的博客论坛系统+源码+开发文档+视频演示,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于java+Spring+...

    JdbcTemplate通用泛型Dao实现

    本文将深入探讨`JdbcTemplate`通用泛型Dao实现的相关知识点,帮助开发者更好地理解和应用这一技术。 首先,让我们了解什么是`JdbcTemplate`。它是Spring框架的一部分,用于处理SQL操作。`JdbcTemplate`提供了一组...

    jdbctemplate+druid连接池.docx

    JdbcTemplate 是 Spring 框架的一部分,提供了一种模板化的 JDBC 操作方式,简化了数据库访问,而 Druid 是阿里巴巴开源的一个高性能、功能丰富的数据库连接池实现。 首先,我们来看一下如何集成 Druid 连接池。在 ...

    基于Maven+JSP+Servlet+JdbcTemplate+Redis+Mysql实现的旅游网站

    本系统是基于Maven+JSP+Servlet+JdbcTemplate+Redis+Mysql实现的旅游网站,使用的技术偏多,但是网站内容很简单,容易理解。包含:景点门票,酒店预订,出境游,国内游,港澳游,报团,自由行等功能.登录,注册,后台管理...

    java 基于泛型与反射的通用 DAO

    在实现通用DAO时,反射通常用于动态调用数据库操作的方法,比如SQL查询。例如,在`UsersDAO.java`中,可能有以下代码: ```java public class UsersDAO extends BaseDao&lt;Users&gt; { @Override public void save...

    Spring jdbctemplate + mysql 分页封装

    在IT行业中,数据库操作是应用开发中的重要环节,Spring框架的JdbcTemplate是Java开发者常用的数据库访问工具,它简化了SQL的执行和结果处理。本文将深入探讨如何利用Spring的JdbcTemplate进行MySQL数据库的分页查询...

    springmvc+jdbctemplate+mchange+multidatasource

    标题 "springmvc+jdbctemplate+mchange+multidatasource" 涉及到的是一个在Java后端开发中常见的技术组合,主要用于构建基于Spring MVC的多数据源应用,并使用JdbcTemplate进行数据库操作,同时利用mchange(通常指...

    springboot+JdbcTemplate+druid

    在本项目中,我们关注的是如何在 Spring Boot 应用程序中集成 JdbcTemplate 和 Druid 数据源来实现数据库操作。 JdbcTemplate 是 Spring 框架的一部分,提供了一个方便的 API 来执行 SQL 查询和更新,避免了手动...

    SpringMvc+JdbcTemplate+oracle的小项目实例

    总的来说,这个"SpringMvc+JdbcTemplate+oracle的小项目实例"是一个很好的学习资源,它涵盖了Web开发的基本流程,包括前后端交互、数据库操作和验证机制。通过这个项目,你可以深入理解这些技术的协同工作方式,为更...

    SpringMvc+jdbcTemplate+mysql(注解)

    Spring MVC支持动态页面参数,可以配合JdbcTemplate的查询方法实现分页。开发者可以通过设置SQL的LIMIT和OFFSET子句来获取特定范围的数据,然后传递这些数据到视图进行渲染。同时,可以使用Spring提供的Pageable接口...

    springboot+jdbctemplate+mysql示例

    一套完成的使用Idea开发环境,采用springboot、jdbcTemplate实现的mysql读取webService完成程序。其中,封装了数据读、写、删除的各种操作;访问webservice时,需要使用token认证。 一套很实用、很难得的的基于java...

    SpringMVC+jdbcTemplate+easyUI+ztree(增删改查)

    在IT行业中,构建Web应用程序是常见的任务,而SpringMVC、jdbcTemplate、easyUI和ztree这四个技术组件常被用于构建高效、易用且功能丰富的管理后台。下面将详细介绍这些技术及其在“增删改查”操作中的应用。 首先...

    struts2+hibernate+spring+jdbctemplate+EXT集成实例

    Struts2、Hibernate、Spring、JdbcTemplate以及EXT是Java Web开发中的重要框架和技术,它们各自在应用程序的不同层面发挥着关键作用。将这些技术整合在一起,可以构建出高效、灵活且可维护的Web应用。 1. **Struts2...

    用maven + spring mvc +JDBCTEMPLATE +由Slf4j实现Common-Logging+Log4j的日志控制(数据库用MySQL)

    本文将探讨如何使用Maven、Spring MVC、JDBCTEMPLATE和日志框架Slf4j实现一个基于MySQL数据库的简单注册功能。首先,我们需要搭建一个开发环境,选用的技术栈包括: 1. **Maven** - 作为项目管理和构建工具,Maven...

    基于 Java+Mysql 实现的图书管理系统( Spring+Spring MVC+JdbcTemplate)

    【作品名称】:基于 Java+Mysql 实现的图书管理系统( Spring+Spring MVC+JdbcTemplate) 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项...

    Springboot+Atomikos+Jpa+Mysql实现JTA分布式事务

    本文将详细讲解如何利用Spring Boot、Atomikos、JPA(Java Persistence API)以及MySQL来实现JTA(Java Transaction API)分布式事务。 首先,Spring Boot是一个轻量级的框架,它简化了基于Spring的应用程序开发...

Global site tag (gtag.js) - Google Analytics