JDBC的项目开发实例<轻量级封装>

    在学习JDBC的时候,项目开发的过程中,总会遇到对数据库表数据增删改查的操作,而很多基本的JDBC代码都会出现大量的重复编写,不仅对DAO的编写造成了繁琐,也对以后的维护增加了困难。当然,很多开源的框架可以解决这些问题,比如Mybatis,Hibernate等这些好的框架可以很好的实现相应的效果,但处在学习基础的过程中,这些框架不建议使用,那么最好的方法就是可以封装原来重复的代码,这样的话,一个可以适用学习的过程,其次也可以提高对Java基础的掌握程度,再而可以通过自己的封装适用最基本的项目开发,而且很容易,很方便的进行自己的项目维护开发。

    注:本博文所使用的代码可以通过以下链接进行获取http://download.csdn.net/detail/songdeitao/6753631

    下面就来看看最基本的JDBC轻量型封装项目的实例。

    首先来看一下最终的效果。

    比如仅仅对数据库中数据增加的DAO操作,代码需要这样写:

    UserDao中实现User数据的新增操作

public boolean doCreate(User user) {
		// 创建标志位
		boolean flag = false;
		// 创建sql语句
		String sql = "insert into t_user values(?,?,?,?,?)";
		// 填充参数(主键不需要填写,自增)
		Object[] parameters = new Object[] { null, user.getUserName(),
				user.getAge(), user.getBirthday(), user.getIsVip() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}
    首先创建sql语句,这里的sql语句需要通过预处理的方式进行操作,通过占位符填写相应的参数,然后将需要的参数(和占位符个数相同)创建成Object对象数组,数组中包含将要对占位符操作的数据,而轻量型封装的DaoHandle这个类,就主要负责了对数据库数据增删改查的操作。

    调用的测试代码这样编写:

/**
	 * 测试增加一条记录到数据库中
	 */
	public static void addUser() {
		// 创建对象
		User user = new User("steven", 23, new Date(), true);
		// 创建一条记录
		boolean flag = userDao.doCreate(user);
		// 是否成功创建记录
		System.out.println(flag);
	}
    这个时候就可以在数据库表中新增一条数据。


    下面就来进行主要代码的实现过程编写:

    本项目中主要采用MyEclipse8.5+Mysql5.0开发环境来实现开发。首先给出项目的结构图,如图1所示:


              图1

    在这个图中,可以看到,这是一个最基本的项目搭建结构,而在这个过程中本文主要对dao进行封装,而所使用的轻量型封装的代码主要是DaoHandle这个类,所以以后的功能扩展,代码可靠性的提高,主要就是对此类进行修改。这里就按照项目搭建的过程一步步讲解。

    首先创建数据库,而用到的数据库代码都在mysql.sql文件,

mysql.sql

--创建数据库steven
create database steven;
--应用数据库
use steven;
--创建表
create table t_user(
	userId	    int(10)	 not null primary key auto_increment,
	userName    varchar(100) not null,
	age	        int(2),
	birthday    datetime,
	isVip       boolean
);
--显示表结构
desc t_user;
--显示表中数据
select * from t_user;
--提交
commit;
    这个时候可以将这些代码如图2所示进行执行:


                        图2

这个时候数据库已经创建成功。

    注:小技巧,在windows的命令提示符中如果想如上图所示改变字体和背景的颜色,可以通过color f4这个命令来实现,不清楚的可以动手试试。后面的f4分别指代的是背景和字体的颜色。

    然后给出User实体的java代码:

User.java

package com.steven.entity;

import java.util.Date;

/**
 * 实体类
 * 
 * @author Steven
 * 
 */
public class User {
	// 用户Id
	private int userId;
	// 用户名
	private String userName;
	// 用户年龄
	private int age;
	// 用户生日
	private Date birthday;
	// 用户是否是会员 true:是 false:不是
	private boolean isVip;

	public User() {
	}

	// 有参构造
	public User(String userName, int age, Date birthday, boolean isVip) {
		this.userName = userName;
		this.age = age;
		this.birthday = birthday;
		this.isVip = isVip;
	}

	public int getUserId() {
		return userId;
	}

	public void setUserId(int userId) {
		this.userId = userId;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public boolean getIsVip() {
		return isVip;
	}

	public void setIsVip(boolean isVip) {
		this.isVip = isVip;
	}

	@Override
	public String toString() {
		return "用户信息: [年龄是" + this.getAge() + ", 生日是" + this.getBirthday()
				+ ", 用户编号是" + this.getUserId() + ", 用户名是" + userName
				+ isVip(this.getIsVip()) + "]";
	}

	public String isVip(boolean isVip) {
		return ", " + (isVip == true ? "是" : "不是") + "会员";
	}

}
       这个时候给出数据库资源文件的代码,这个资源文件的获取有很多种方式,感兴趣的可以参照(http://blog.csdn.net/songdeitao/article/details/17447627)这篇博文进行学习。

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/steven
user=root
password=mysqladmin

         连接Mysql数据库的代码,这里的连接驱动的jar包已经附属到项目中,

DatabaseConnection.java

package com.steven.dbc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 进行数据库连接的操作
 * 
 * @author Steven
 * 
 */
public class DatabaseConnection {
	public static String driver;// 驱动
	public static String url;// url
	public static String user;// 用户名
	public static String password;// 密码

	public DatabaseConnection() {

	}

	static {
		File file = new File("src/jdbc.properties");
		Properties pro = new Properties();
		try {
			FileInputStream in = new FileInputStream(file);
			pro.load(in);// 读取属性配置文件
			driver = pro.getProperty("driver");
			url = pro.getProperty("url");
			user = pro.getProperty("user");
			password = pro.getProperty("password");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取数据库连接
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		Connection conn = null;
		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url, user, password);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}

	/**
	 * 关闭数据库对象
	 * 
	 * @param con
	 * @param stmt
	 * @param rs
	 */
	public static void closeAll(Connection con, Statement stmt, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			stmt = null;
		}
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			con = null;
		}
	}

	/**
	 * 测试数据库连接
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Connection conn = DatabaseConnection.getConnection();
		System.out.println(conn);
	}
}
    注:一般情况下如果写完了连接数据库的代码,都要进行一次测试,如果成功可以进行接下来的编码,如果失败了,就要进行错误纠正,这样可以保证以后的代码编写不会因为之前的错误而影响到,也减少了更改bug的难度。这种递进式的代码开发,是常用的开发模式。尤其在大的项目中,一定要对自己的每一个模块可以进行测试,确保项目的正确性。
    测试结果:

com.mysql.jdbc.JDBC4Connection@f11404
获取了连接对象,是正确的。 
    下面将给出博文的核心,也是轻量型JDBC代码的封装的具体实现,DaoHandle类的具体实现如下所示:

DaoHandle.java

package com.steven.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.steven.dbc.DatabaseConnection;

/**
 * DAO数据操作的辅助类
 * 
 * @author Steven
 * 
 */
public class DaoHandle {
	private static Connection con;
	private static PreparedStatement pstmt;
	private static ResultSet rs;

	/**
	 * 执行所有的DML操作
	 * 
	 * @param sql
	 * @param parameters
	 * @return
	 */
	public static int executeDML(String sql, Object[] parameters) {
		int count = -1;
		// 获取连接
		con = DatabaseConnection.getConnection();

		if (con != null) {

			try {
				// 获取处理器对象
				pstmt = con.prepareStatement(sql);
				// 注入参数
				for (int i = 0; i < parameters.length; i++) {
					// 根据参数的类型判断调用注入方法
					// 参数类型不安全
					pstmt.setObject(i + 1, parameters[i]);
				}
				// 执行SQL语句
				count = pstmt.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, null);
			}
		}
		return count;
	}

	/**
	 * 查询获取单个对象
	 * 
	 * @param <T>
	 * @param sql
	 * @param paramters
	 * @param objClass
	 * @return
	 */
	public static <T> T executeQueryForSingle(String sql, Object[] parameters,
			Class<T> objClass) {
		T t = null;
		// 获取连接
		con = DatabaseConnection.getConnection();

		if (con != null) {
			try {
				// 获取处理器
				pstmt = con.prepareStatement(sql);
				// 注入参数
				if (parameters != null) {
					for (int i = 0; i < parameters.length; i++) {
						pstmt.setObject(i + 1, parameters[i]);
					}
				}
				// 执行查询
				rs = pstmt.executeQuery();
				// 获取结果集的元数据
				ResultSetMetaData metaData = rs.getMetaData();
				// 获取所有列名
				String[] colNames = getColNames(metaData);
				// 获取所有列的数据类型
				// int[] colTypes = getColTypes(metaData);
				// 使用反射获取当前类的方法
				Method[] methods = objClass.getDeclaredMethods();
				// 获取结果集的数据
				while (rs.next()) {
					// 获取类的实例
					t = objClass.newInstance();
					// 循环判断每列的数据类型
					for (int i = 0; i < colNames.length; i++) {
						// 获取每列的结果
						Object value = null;
						// 进行所有参数类型的赋值,不能保证类型安全
						value = rs.getObject(i + 1);
						// 遍历每个方法
						for (Method m : methods) {
							if (value != null) {
								// 如果是和该列同名的set方法,则调用该方法
								if (m.getName().equalsIgnoreCase(
										"set" + colNames[i])) {
									// 进行对set方法的调用,向其中置值
									m.invoke(t, value);
								}
							}
						}

					}
				}

			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, rs);
			}
		}
		// 返回对象
		return t;
	}

	/**
	 * 通过元数据获取列名数组
	 * 
	 * @param metaData
	 * @return
	 */
	private static String[] getColNames(ResultSetMetaData metaData) {
		String[] colNames = null;

		try {
			// 获取结果集的列数
			int colCount = metaData.getColumnCount();
			// 创建数组
			colNames = new String[metaData.getColumnCount()];
			// 遍历每列
			for (int i = 1; i <= colCount; i++) {
				// 获取列名
				colNames[i - 1] = metaData.getColumnLabel(i);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return colNames;
	}

	/**
	 * 根据元数据获取所有列的数据类型
	 * 
	 * @param metaData
	 * @return
	 */
	private static int[] getColTypes(ResultSetMetaData metaData) {
		int[] colNames = null;

		try {
			// 获取列数
			int colCount = metaData.getColumnCount();
			// 创建数组
			colNames = new int[colCount];
			for (int i = 1; i <= colCount; i++) {
				// colNames[i-1] = metaData.getColumnTypeName(i);
				colNames[i - 1] = metaData.getColumnType(i);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return colNames;
	}

	/**
	 * 查询多行对象
	 * 
	 * @param <T>
	 * @param sql
	 * @param paramters
	 * @param objClass
	 * @return
	 */
	public static <T> List<T> executeQueryForMultiple(String sql,
			Object[] parameters, Class<T> objClass) {
		List<T> list = new ArrayList<T>();

		// 获取连接
		con = DatabaseConnection.getConnection();

		if (con != null) {
			try {
				// 获取处理器
				pstmt = con.prepareStatement(sql);
				if (parameters != null) {
					// 注入参数
					for (int i = 0; i < parameters.length; i++) {
						pstmt.setObject(i + 1, parameters[i]);
					}
				}
				// 执行查询
				rs = pstmt.executeQuery();
				// 获取结果集的元数据
				ResultSetMetaData metaData = rs.getMetaData();
				// 获取所有列名
				String[] colNames = getColNames(metaData);
				// 获取所有列的数据类型
				// int[] colTypes = getColTypes(metaData);
				// 使用反射获取当前类的方法
				Method[] methods = objClass.getDeclaredMethods();
				// 获取结果集的数据
				while (rs.next()) {
					T t = null;
					// 获取类的实例
					t = objClass.newInstance();
					// 循环判断每列的数据类型
					for (int i = 0; i < colNames.length; i++) {
						// 获取每列的结果
						Object value = null;
						// 进行所有参数类型的赋值,不能保证类型安全
						value = rs.getObject(i + 1);
						// 遍历每个方法
						for (Method m : methods) {
							if (value != null) {
								// 如果是和该列同名的set方法,则调用该方法
								if (m.getName().equalsIgnoreCase(
										"set" + colNames[i])) {
									// 进行对set方法的调用,向其中置值
									m.invoke(t, value);
								}
							}
						}

					}
					// 向集合中添加数据
					list.add(t);
				}

			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} finally {
				DatabaseConnection.closeAll(con, pstmt, rs);
			}
		}
		// 返回集合
		return list;
	}

	/**
	 * 返回数据表中数据的条数
	 * 
	 * @param sql
	 * @param paramters
	 * @return
	 */
	public static int executeQueryForCount(String sql, Object[] parameters) {
		int count = 0;
		// 获取连接
		con = DatabaseConnection.getConnection();
		try {
			// 获取处理器
			pstmt = con.prepareStatement(sql);
			if (parameters != null) {
				// 注入参数
				for (int i = 0; i < parameters.length; i++) {
					pstmt.setObject(i + 1, parameters[i]);
				}
			}
			// 执行查询
			rs = pstmt.executeQuery();
			// 遍历查找的结果集
			while (rs.next()) {
				count = rs.getInt(1);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DatabaseConnection.closeAll(con, pstmt, rs);
		}
		return count;
	}
}

       注:这个过程中用到了反射机制,也是java底层代码实现的最基本应用,功能很强大,通过相应的方法,可以进行动态的方法调用,实现数据库表元素和对象之间的互相映射。其实在数据库框架中,也大量使用这反射机制。

    然后给出Dao层中,iface和impl的实现代码:

IBaseDao.java

package com.steven.dao.iface;

import java.util.List;

/**
 * 通过泛型机制为所有的实现类提供一下方法
 * 
 * @author Steven
 * 
 */
public interface IBaseDao<T> {
	/**
	 * 新增一条数据
	 * 
	 * @param t
	 * @return
	 */
	public boolean doCreate(T t);

	/**
	 * 根据编号(主键)进行删除一条数据
	 * 
	 * @param id
	 * @return
	 */
	public boolean delete(int id);

	/**
	 * 修改数据
	 * 
	 * @param t
	 * @return
	 */
	public boolean update(T t);

	/**
	 * 根据ID查找到该信息
	 * 
	 * @param id
	 * @return
	 */
	public T findById(int id);
	
	/**
	 * 返回所有的信息
	 * @return
	 */
	public List<T> findAll();
}
IUserDao.java

package com.steven.dao.iface;

import com.steven.entity.User;

public interface IUserDao extends IBaseDao<User> {

}
UserDao.java

package com.steven.dao.impl;

import java.util.List;

import com.steven.dao.iface.IUserDao;
import com.steven.entity.User;
import com.steven.util.DaoHandle;

public class UserDao implements IUserDao {

	@Override
	public boolean delete(int id) {
		// 创建标志位
		boolean flag = false;
		// 创建sql语句
		String sql = "delete from t_user where userId=?";
		// 填充参数
		Object[] parameters = new Object[] { id };
		// 执行数据库数据删除操作
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}

	@Override
	public boolean doCreate(User user) {
		// 创建标志位
		boolean flag = false;
		// 创建sql语句
		String sql = "insert into t_user values(?,?,?,?,?)";
		// 填充参数(主键不需要填写,自增)
		Object[] parameters = new Object[] { null, user.getUserName(),
				user.getAge(), user.getBirthday(), user.getIsVip() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}

	@Override
	public List<User> findAll() {
		// 创建sql语句
		String sql = "select * from t_user";
		// 查询所有的记录
		List<User> list = (List<User>) DaoHandle.executeQueryForMultiple(sql,
				null, User.class);
		return list;
	}

	@Override
	public User findById(int id) {
		// 创建sql语句
		String sql = "select * from t_user where userId = ?";
		// 填充参数
		Object[] parameters = new Object[] { id };
		// 查找单个记录
		User user = DaoHandle
				.executeQueryForSingle(sql, parameters, User.class);
		return user;
	}

	@Override
	public boolean update(User user) {
		// 创建标志位
		boolean flag = false;
		// 创建sql语句
		String sql = "update t_user set userName=?,age=?,birthday=?,isVip=? where userId=?";
		// 注入参数
		Object[] parameters = new Object[] { user.getUserName(), user.getAge(),
				user.getBirthday(), user.getIsVip(), user.getUserId() };
		flag = DaoHandle.executeDML(sql, parameters) > 0 ? true : false;
		return flag;
	}
}
接下来将给出UserDao的测试代码,也是对轻量型封装的DaoHandle的检验,

CRUDTest.java

package com.steven.test;

import java.util.Date;
import java.util.List;

import com.steven.dao.impl.UserDao;
import com.steven.entity.User;

public class CRUDTest {
	private static UserDao userDao = new UserDao();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// addUser();
		// retrieveUser();
		// deleteUser();
		// updateUser();
		// retrieveAll();
	}

	/**
	 * 测试增加一条记录到数据库中
	 */
	public static void addUser() {
		// 创建对象
		User user = new User("steven", 23, new Date(), true);
		// 创建一条记录
		boolean flag = userDao.doCreate(user);
		// 是否成功创建记录
		System.out.println(flag);
	}

	/**
	 * 测试从数据库中读取一条记录
	 */
	public static void retrieveUser() {
		// 从数据库中读取一条记录
		User user = userDao.findById(1);
		// 将此记录的相应对象输出
		System.out.println(user);
	}

	/**
	 * 从数据库表中删除一条数据
	 */
	public static void deleteUser() {
		// 首先从数据库中查找出一条记录
		User user = userDao.findById(1);
		// 然后根据查找后的对象id从数据库中删除此记录
		boolean flag = false;
		if (user != null) {
			flag = userDao.delete(user.getUserId());
		}
		// 输出删除的成功与否
		System.out.println(flag);
	}

	/**
	 * 更新数据操作
	 */
	public static void updateUser() {
		// 从数据表中读取一条数据
		User user = userDao.findById(2);
		// 更改数据
		user.setIsVip(false);
		// 执行更新操作
		boolean flag = userDao.update(user);
		// 成功与否
		System.out.println(flag);
	}

	public static void retrieveAll() {
		// 获取所有记录的集合
		List<User> userList = userDao.findAll();
		// 输出所有对象信息
		for (User user : userList) {
			System.out.println(user);
		}
	}
}

然后给出测试的结果,这里仅仅给出前两个测试效果,所有的测试都是正确的,可以自己动手试试。

addUser()

控制台输出

true
数据库中的结果如图3所示:


                       图3

retrieveUser();

控制台输出:

用户信息: [年龄是23, 生日是2013-12-22 16:10:25.0, 用户编号是1, 用户名是steven, 是会员]

    到这里,轻量型的JDBC封装的实现和测试已经全部给出,下面就开始编写JavaSE的具体项目逻辑了,这时候可以在建一个包com.steven.business层(可以为其他的层次)进行业务逻辑的操作,这样一个简答的使用封装后的JDBC项目就可以实现了,当然这也可以用到web项目中,具体的项目编写,还是大家实现吧,这个封装仅仅是简单的实现,如果有需要扩展的,比如分页查找,搜索字段,都是可以实现的。

    在此恭祝大家学习愉快!


  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值