在学习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项目中,具体的项目编写,还是大家实现吧,这个封装仅仅是简单的实现,如果有需要扩展的,比如分页查找,搜索字段,都是可以实现的。
在此恭祝大家学习愉快!