????????因为最近学习了dbutils,感觉多数的表都用共同的操作(增删改查),刚好也闲,就顺手做了个BaseDao,dao层的类只需要继承BaseDaoImpl类就可以用使用基本的增删改查。
用前须知(请一定要仔细观看):
作用一,实现基本的数据库操作 (BaseDaoImpl)
用法:(本人用的mysql数据库,如果数据库不同,要更改数据库驱动jar包和xml对应的驱动名和值) 1、复制jar包和c3p0-config.xml到项目的src目录下 2、建数据库和表 3、修改,配置c3p0-config.xml文件 4、Dao层的类继承cn.hnzj.hhao.dao.impl.BaseBaoImpl类(下文会提供源码) 5、编写表对应的bean类 注: (1)继承时的泛型为表对应的bean类 (2)bean类要求:字段名和数量与数据库表对应,id属性名需为:(id名可以是id 或 <表名>id 或 <表名的首字母>id 或 <表名的首字母>-id ? 不区分大小写) (3)如果bean类名与数据库的表名不同,需要调用父类的有参构造方法(参数为String类型<表名>),设置表名 (3)bean字段的fianl字段不会被识别。
作用二,(C3P0Utils) 1、提供数据源对象 2、提供数据库连接对象 3、提供QueryRunner对象 4、查询当前数据库下的所有表名 5、查询某个表的所有字段
用法:直接用C3P0Utils.<方法>
jar包依赖:
c3p0-0.9.5.5.jar
commons-dbutils-1.7.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.16.jar
c3p0-config.xml c3p0的配置文件(必须放在src根目录下,名称也不能改)
<?xml version="1.0" encoding="UTF-8"?>
<!-- 文件名 -->
<c3p0-config>
<default-config>
<!-- 数据库驱动名 -->
<property name="driverClass" >com.mysql.cj.jdbc.Driver</property>
<!-- 数据库的url -->
<property name="jdbcUrl" >jdbc:mysql://localhost:3306/mysql?serverTimeZone=UTC</property>
<!-- 数据库名称 -->
<property name="user" >root</property>
<!-- 数据库密码 -->
<property name="password" >123456</property>
<!-- chechoutTimeout:等三秒检查 -->
<property name="checkoutTimeout" >3000</property>
<!-- 初始化时的连接数量 -->
<property name="initialPoolSize" >10</property>
<!-- 连接池内最大的连接数量 -->
<property name="maxPoolSize" >100</property>
<!-- 连接池内最小的连接数量 -->
<property name="minPoolSize" >10</property>
<!-- 连接池中缓存PrepareStatement的总数 -->
<property name="maxStatements" >200</property>
</default-config>
<!-- test数据库 -->
<named-config name="test">
<property name="driverClass" >com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl" >jdbc:mysql://localhost:3306/test?serverTimezone=UTC</property>
<property name="user" >root</property>
<property name="password" >123456</property>
<property name="checkoutTimeout" >3000</property>
<property name="initialPoolSize" >10</property>
<property name="maxPoolSize" >10</property>
</named-config>
</c3p0-config>
下方为源代码:
BaseDao:
package cn.hnzj.hhao.dao;
import java.util.List;
/**
* Title: BaseDao Description: 实现数据库的基本操作
*
* @author HhaoAn
* @date 2021年12月10日
*/
public interface BaseDao<T> {
/**
* Title: insert Description: 通过对象插入数据
*/
int insert(T t) throws Exception;
/**
* Title: update Description: 通过对象更改对应id的数据
*/
int update(T t) throws Exception;
/**
* Title: delete Description: 通过对象的id属性删除数据
*/
int delete(T t) throws Exception;
/**
* Title: selectById Description: 通过对象的id的属性查询数据
*/
T selectById(T t) throws Exception;
/**
* Title: selectAll Description: 查询所有的数据
*/
List<T> selectAll() throws Exception;
}
????????注:会抛出自定义异常,下方有自定义异常类的源代码(我认为,抛出中文的异常说明还是好看懂,所以顺手做了异常)。?
BaseDaoImpl:
package cn.hnzj.hhao.dao.impl;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.hnzj.hhao.dao.BaseDao;
import cn.hnzj.hhao.exception.FieldDaoException;
import cn.hnzj.hhao.exception.TableNameDaoException;
import cn.hnzj.hhao.exception.DaoException;
import cn.hnzj.hhao.util.C3P0Utils;
/**
* Title: BaseDao Description: 进行数据库的基本操作
* 用法:子类继承数据连接层,继承此类,泛型用要操作的表对应的bean类。并保证Bean对象和数据库的字段名一致
* (id,<表名>id,<表名的首字母>id,<表名的首字母>-id 不区分大小写)
*
* 比如:user表对应的bean类为User则泛型写<User> 《User [id,username,userpassword]》,《User
* [userid,username,userpassword]》,《User [userId,username,userpassword]》,《User
* [uId,username,userpassword]》,《User [u-Id,username,userpassword]》
*
* 如果类名与表名不对应: 泛型依旧用对应的Bean类,但是要重新设置表名字段:在子类调用父类的有参构造方法中传入表名
*
* 如:public UserDaoImpl() throws DaoException { super("user"); }
*
* @author HhaoAn
* @date 2021年12月9日
*/
public class BaseDaoImpl<T> extends Thread implements BaseDao<T> {
// 操作语句常量
private static final String INSERT = "insert";
private static final String UPDATE = "update";
private static final String DELETE = "delete";
private static final String SELECTBYID = "selectbyid";
private static final String SELECTALL = "selectall";
/** runner:执行sql操作 */
private static QueryRunner runner = null;
/** entityClass:子类的类 */
private Class<T> entityClass = null;
/** tableName:子类的类名(表名) */
private String tableName = null;
/** idIndex:子类表的id下标 */
private int idIndex = 0;
private Field[] fields = null;
public BaseDaoImpl() {// 创建子类对象时会调用父类的无参构造方法,从而实现赋值
init();// 初始化信息
try {
// 检测标准
fieldCheck();
} catch (DaoException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Title: Description: 提供表名,修改泛型的表名
*
* @param tableName
*/
public BaseDaoImpl(String tableName) {// 创建子类对象时会调用父类的无参构造方法,从而实现赋值
init();// 初始化信息
this.tableName = tableName.toLowerCase();
System.out.println(this.tableName);
try {
// 检测标准
fieldCheck();
} catch (DaoException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Title: insert Description: 通过对象插入数据
*
* @param 需要添加的对象
* @return 执行后,影响的行数
*/
@Override
public int insert(T t) {
int row = -1;
try {
row = runner.update(getSql(INSERT), setArgs(INSERT, t));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return row;
}
/**
* Title: update Description: 通过对象更改对应id的数据
*
* @param 需要修改的对象(需要含有id值)
* @return 执行后,影响的行数
*/
@Override
public int update(T t) {
int row = -1;
try {
row = runner.update(getSql(UPDATE), setArgs(UPDATE, t));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return row;
}
/**
* Title: delete Description: 通过对象的id属性删除数据
*
* @param 需要删除的对象(需要含有id值)
* @return 执行后,影响的行数
*/
@Override
public int delete(T t) {
int row = -1;
try {
return runner.update(getSql(DELETE), setArgs(DELETE, t));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return row;
}
/**
* Title: selectById Description: 通过对象的id的属性查询数据
*
* @param 需要查询的对象
* @return 执行后,查找的一个对象
*/
@Override
public T selectById(T t) {
try {
t = runner.query(getSql(SELECTBYID), new BeanHandler<T>(entityClass), setArgs(SELECTBYID, t));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return t;
}
/**
* Title: selectAll Description: 查询所有的数据
*
* @param 需要查询的对象
* @return 执行后,对应表的所有信息
*/
@Override
public List<T> selectAll() {
List<T> result = null;
try {
result = runner.query(getSql(SELECTALL), new BeanListHandler<T>(entityClass));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* Title: getSql Description: 获取sql语句
*
* @throws Exception
*/
private String getSql(String operation) throws Exception {
StringBuffer sql = new StringBuffer();
switch (operation) {
case INSERT:// 插入语句 insert into <table> values(?,?...);
sql.append("insert into ").append(tableName).append(" values(");
for (int i = 0; i < fields.length; i++)
sql.append("?,");
sql.deleteCharAt(sql.length() - 1);
sql.append(");");
break;
case UPDATE:// 修改语句 update <table> set *=?,*=?... where <id>=?;
sql.append("update ").append(tableName).append(" set ");
for (int i = 0; i < fields.length; i++)
if (i != idIndex)
sql.append(fields[i].getName()).append("=?,");
sql.deleteCharAt(sql.length() - 1);
sql.append(" where ").append(fields[idIndex].getName()).append("=?;");
break;
case DELETE:// 删除语句 delete from <table> where <id>=?;
sql.append("delete from ").append(tableName).append(" where ").append(fields[idIndex].getName())
.append("=?;");
break;
case SELECTBYID:// 通过id查询语句 select * from <table> where <id>=?;
sql.append("select * from ").append(tableName).append(" where ").append(fields[idIndex].getName())
.append("=?;");
break;
case SELECTALL:// 查询全部语句 select * from <table>
sql.append("select * from ").append(tableName);
break;
default:// 操作参数传入异常,产生错误,阻止程序继续运行
throw new Exception("getSql方法的传入参数有误");
}
return sql.toString();
}
/**
* Title: setArgs Description: 把参数包装为数组
*
* @throws Exception
*/
private Object[] setArgs(String operation, T t) throws Exception {
Object[] args = null;
switch (operation) {
case INSERT:// 插入语句 [*,*,*...]
args = new Object[fields.length];
for (int i = 0; i < args.length; i++) {
fields[i].setAccessible(true);
args[i] = fields[i].get(t);
}
break;
case UPDATE:// 修改语句 [*,*,*...,<id>]
args = new Object[fields.length];
for (int i = 0; i < args.length; i++) {
fields[i].setAccessible(true);
if (i < idIndex)
args[i] = fields[i].get(t);
else if (i == idIndex)
args[args.length - 1] = fields[idIndex].get(t);
else
args[i - 1] = fields[i].get(t);
}
break;
case DELETE:// 删除语句 删除和通过id查询都只需要<id>参数,可以共用
case SELECTBYID:// 通过id查询语句
fields[idIndex].setAccessible(true);
args = new Object[] { fields[idIndex].get(t) };
break;
default:// 操作参数传入异常,产生错误,阻止程序继续运行
throw new Exception("setArgs方法的第一个传入参数有误");
}
return args;
}
/**
* Title: init Description: 初始化信息
*/
@SuppressWarnings("unchecked") // 不报错提示
private void init() {
// 获取QueryRunner对象,进行sql语句操作
runner = C3P0Utils.getQueryRunner();
// 获取泛型参数
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();// this.getClass()等价于getClass();
// 获取泛型的类
entityClass = (Class<T>) type.getActualTypeArguments()[0];
// 获取子类的表名 .substring(0, getClass().getSimpleName().length() - 4)
tableName = entityClass.getSimpleName().toLowerCase();
// 排除常量字段
List<Field> var = new ArrayList<Field>();
for (Field field : entityClass.getDeclaredFields()) {// 如果不是fianl修饰字段,则添加到列表中
if (!java.lang.reflect.Modifier.isFinal(field.getModifiers()))
var.add(field);
}
fields = new Field[var.size()];
fields = var.toArray(fields);
}
/**
* Title: fieldCheck Description: 检查格式是否匹配
*/
private void fieldCheck() throws DaoException {
List<String> tableNames = C3P0Utils.getTableNames();
// 判断表名是否存在
if (!tableNames.contains(tableName))
throw new TableNameDaoException(tableNames, tableName);
List<String> columnNames = C3P0Utils.getColumnNames(tableName);
List<String> fields = new ArrayList<String>();
for (Field field : this.fields)
fields.add(field.getName());
// 判断字段名是否匹配
if (!(fields.containsAll(columnNames) && columnNames.containsAll(fields)))
throw new FieldDaoException(entityClass.getSimpleName(), tableName, fields, columnNames);
// 获取id属性值的下标
for (String field : fields) {
if (field.equalsIgnoreCase("id") || field.equalsIgnoreCase(tableName + "id")
|| field.equalsIgnoreCase(tableName.substring(0, 1) + "id")
|| field.equalsIgnoreCase(tableName.substring(0, 1) + "-" + "id"))// 存在时,直接结束
break;
idIndex++;// 下标加以
}
// 下标等于字段数组的长度时,说明没有找到id属性,报错
if (idIndex == this.fields.length)
throw new FieldDaoException(entityClass.getSimpleName());
}
}
异常类:(处理BaseDaoImpl抛出的异常)
DaoException:自定义报错的父类
package cn.hnzj.hhao.exception;
/**
* Title: DaoException Description: 数据连接层的基本报错
*
* @author HhaoAn
* @date 2021年12月12日
*/
public class DaoException extends Exception {
/** serialVersionUID: */
private static final long serialVersionUID = 1L;
public DaoException() {
// TODO Auto-generated constructor stub
super("数据库表结构和Bean类不匹配或无法连接数据库");
}
public DaoException(String info) {
// TODO Auto-generated constructor stub
super(info);
}
}
C3P0ParamDaoException: 数据库连接的报错
package cn.hnzj.hhao.exception;
/**
* Title: ParamDaoException Description: 数据库没有成功连接时的报错
*
* @author HhaoAn
* @date 2021年12月10日
*/
public class C3P0ParamDaoException extends DaoException {
/** serialVersionUID: */
private static final long serialVersionUID = 1L;
/**
* Title: Description: 报错信息
*/
public C3P0ParamDaoException() {
// TODO Auto-generated constructor stub
super("无法连接数据库,请检查您的c3p0-config文件内的参数是否正确的与数据库匹配,或更改了c3p0-config文件的名称,位置等");
}
}
FieldDaoException:字段是否匹配的报错
package cn.hnzj.hhao.exception;
import java.util.List;
/**
* Title: FieldDaoException Description: 数据库与bean类字段问题
*
* @author HhaoAn
* @date 2021年12月12日
*/
public class FieldDaoException extends DaoException {
/** serialVersionUID: */
private static final long serialVersionUID = 1L;
public FieldDaoException(String beanName) {
// TODO Auto-generated constructor stub
super("您的" + beanName + "类的id属性名与默认的id名(id,<表名>id,<表名的首字母>id,<表名的首字母>-id 不区分大小写)不匹配");
}
public FieldDaoException(String beanName, String tableName, List<String> fields, List<String> columnNames) {
// TODO Auto-generated constructor stub
super("您的" + beanName + "类属性名有:" + fields + "与数据库内" + tableName + "表的字段名:" + columnNames + "数量或名称不匹配");
}
}
RunSqlDaoException:运行时传入参数的报错(基本上时不可能遇到)
package cn.hnzj.hhao.exception;
public class RunSqlDaoException extends DaoException{
/** serialVersionUID:*/
private static final long serialVersionUID = 1L;
public RunSqlDaoException(Object obj) {
// TODO Auto-generated constructor stub
super("请检查:"+obj+"的参数是否合法(类型不匹配或id为空值)");
}
}
TableNameDaoException:表名不匹配时的报错
package cn.hnzj.hhao.exception;
import java.util.List;
/**
* Title: TableNameDaoException Description: 未在相应数据库下找到表名
*
* @author HhaoAn
* @date 2021年12月12日
*/
public class TableNameDaoException extends DaoException {
/** serialVersionUID: */
private static final long serialVersionUID = 1L;
public TableNameDaoException() {
// TODO Auto-generated constructor stub
super("表名与数据库内表名不对应(如果您未设置表名需要bean类名与表名相同,或在继承类上设置tableName属性,或当前的数据库下并没有这个表)");
}
public TableNameDaoException(List<String> tableNames, String tableName) {
// TODO Auto-generated constructor stub
super("未读取到对应的表,当前数据库下存在表有:" + tableNames + "当前读取到的表名为:" + tableName + "(如果您需要设置" + tableName
+ "的类名与数据库内的表名相同,或在继承BaseBean的类的构造方法上调用父类的有参构造方法(设置表名))");
}
}
C3P0Utils:数据源的工具类
package cn.hnzj.hhao.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import cn.hnzj.hhao.exception.C3P0ParamDaoException;
/**
* Title: C3P0Utils Description: 获取数据库连接 记得根据自己的数据库调整c3p0-config.xml的参数
*
* @author HhaoAn
* @date 2021年12月6日
*/
public class C3P0Utils {
/** ds:声明一个数据源 */
public static DataSource ds;
static {
ds = new ComboPooledDataSource("test");// 创建一个test数据库数据源对象
}
/**
* Title: getDataSource Description: 获取数据源
*
* @return DataSource
*/
public static DataSource getDataSource() {
return ds;
}
/**
* Title: getConnection Description: 获取一个连接
*
* @return Connection
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* Title: getQueryRunner Description: 获取一个QueryRunner对象
*
* @return QueryRunner
*/
public static QueryRunner getQueryRunner() {
return new QueryRunner(ds);
}
/**
* Title: getColumnNames Description: 获取数据库下的所有表名
*
* @return List<String>
* @throws C3P0ParamDaoException
*/
public static List<String> getTableNames() throws C3P0ParamDaoException {
try {
return getQueryRunner().query("show tables;", new ColumnListHandler<String>());
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
throw new C3P0ParamDaoException();
}
}
/**
* Title: getColumnNames Description: 获取表中所有字段名称
*
* @param tableName 表名
* @return List<String>
* @throws C3P0ParamDaoException
*/
public static List<String> getColumnNames(String tableName) throws C3P0ParamDaoException {
try {
return getQueryRunner().query("desc " + tableName, new ColumnListHandler<String>());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new C3P0ParamDaoException();
}
}
}
以下为自己测试时用的代码:
?UserBean:用户表对应的封装对象
package cn.hnzj.hhao.bean;
import java.io.Serializable;
/**
* Title: user Description: userbean
*
* @author HhaoAn
* @date 2021年12月6日
*/
public class UserBean implements Serializable {
/** serialVersionUID: */
private static final long serialVersionUID = 1L;
private int userid;
private String username;
private String password;
/**
* Title: Description:
*/
public UserBean() {
super();
}
/**
* Title: Description:
*
* @param userid
* @param username
* @param password
*/
public UserBean(int userid, String username, String password) {
super();
this.userid = userid;
this.username = username;
this.password = password;
}
/**
* @return the userid
*/
public int getUserid() {
return userid;
}
/**
* @param userid the userid to set
*/
public void setUserid(int userid) {
this.userid = userid;
}
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/*
* (non-Javadoc) Title: toString Description:
*
* @return
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "UserBean [userid=" + userid + ", username=" + username + ", password=" + password + "]";
}
}
UserDao:用户数据库操作的对应类
package cn.hnzj.hhao.dao;
import java.sql.SQLException;
import cn.hnzj.hhao.bean.UserBean;
/**
* Title: UserDao Description: 实现用户表的基本操作
* @author HhaoAn
* @date 2021年12月10日
*/
public interface UserDao extends BaseDao<UserBean>{
/**
* Title: selectByUserName Description: 通过用户名查询用户
*/
UserBean selectByUserName(UserBean user) throws SQLException;
}
UserDaoImpl:用户数据库操作的对应类(因为Bean名与表名不对应,所以需要调用父类的有参构造方法设置tableName)
package cn.hnzj.hhao.dao.impl;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.hnzj.hhao.dao.UserDao;
import cn.hnzj.hhao.bean.UserBean;
import cn.hnzj.hhao.util.C3P0Utils;
public class UserDaoImpl extends BaseDaoImpl<UserBean> implements UserDao {
public UserDaoImpl() {
// TODO Auto-generated constructor stub
super("User");
}
/**
* Title: selectByUserName Description: 通过用户名查询用户
*/
@Override
public UserBean selectByUserName(UserBean user) throws SQLException {
return new QueryRunner(C3P0Utils.getDataSource()).query("select * from user where username=?",
new BeanHandler<UserBean>(UserBean.class), user.getUsername());
}
public static void main(String[] args) {
System.out.println(new UserDaoImpl().selectAll());
}
}
????????以上便是全部内容,本人也时学生,还处在学习阶段。如果您有更高的见解,或发现bug,或有更好的优化等,都欢迎您的评论,讨论,希望能和您一起进步!!
|