DAO模式
DAO数据访问对象data access object的缩写,用于进行数据操作的封装,在Java程序开发中属于标准开发架构中的持久层的设计模式。
DAO模式是标准JavaEE设计模式之一,用途在于将底层的数据访问操作和高层的业务逻辑分离开。 典型的DAO模式的组成
-
一个DAO工厂类
-
一个DAO接口
-
实现了DAO接口的多种实现类
-
数据传输对象DTO,有时简称为VO
一个接口,通过接口暴漏当前层中允许调用的方法
public interface IUserDao {
public int save(User user);
public int delete(Long id);
public int update(User user);//按照id进行修改
public List<User> query(User user);//user中的非空属性就是查询条件,如果user为null,则查询所有
}
数据传输对象,在DAO中经常使用实体类来代替
public class User implements Serializable { //建议实体类的名称最好和表名称对应
private Long id; //JavaBean的建议规则:私有属性,共有的get/set方法
private String username;
private String password;
private Date createTime; //引入驼峰记法
针对DAO访问接口可以有多种实现,最终希望可以在多种实现之间任意切换。例如MySQLUserDaoImpl 是MySQL的实现,OracleUserDaoImpl是针对接口的oracle实现类
public class UserDaoImpl implements IUserDao{
//其它部分自行实现-- 作业
// 按照id值修改用户名或者口令或者用户名和口令,所以sql语句应该是动态的,按照非空属性
进行生成
public int update(User user) {
if (user == null)
throw new IllegalArgumentException("参数不允许为空!");
if (user.getId() == null)
throw new IllegalArgumentException("参数不允许为空!");
int res = 0;
List<Object> params = new ArrayList<>();
StringBuilder sb = new StringBuilder("update tb_users set id=?,");
params.add(user.getId());
if (user.getUsername() != null) {
// 需要判断新用户名是否重复
List<User> userList = this.queryByName(user.getUsername());
if (userList != null && userList.size() > 0) {
User tmp = userList.get(0);
if (tmp.getId() != user.getId() &&
tmp.getUsername().equals(tmp.getUsername()))
throw new IllegalArgumentException("用户名称不允许重复!");
}
sb.append(" username=?,");
params.add(user.getUsername());
}
if (user.getPassword() != null) {
sb.append("password=?,");
params.add(user.getPassword());
}
if (sb.toString().endsWith(","))
sb.deleteCharAt(sb.length() - 1);
sb.append(" where id=?");
params.add(user.getId());
String sql = sb.toString();
System.out.println(sql);
Connection conn = null;
try {
conn = JdbcUtil.getConnection();
res = JdbcUtil.executeUpdate(conn, sql, params.toArray());
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage());
} finally {
try {
JdbcUtil.close(null, conn);
} catch (Exception e) {
e.printStackTrace();
}
}
return res;
}
}
最后提供一个工厂类,用于创建具体实现对象,可以通过参数在多种实现之间切换
public class DaoFactory{
public static IUserDao getUserDao(String key){
IUserDao userDao=null;
switch(key){
case "mysql":
userDao=new MySQLUserDaoImpl();
break;
case "oracle":
userDao=new OracleUserDaoImpl();
break;
default:
userDao=new SqlServerUserDaoImpl();
break;
}
return userDao;
}
}
DAO模式
一般的DAO最少需要实现CRUD方法
- 一个接口、若干实现、一个工厂、一个值bean 接口定义
public interface IBaseDao<T extends Serializable> {
public int save(T t);
public int delete(T t);
public int update(T t);
public List<T> query(String sql,Object...params);
}
父类
public abstract class BaseDaoImpl<T extends Serializable> implements
IBaseDao<T> {
private Class<T> clazz;
@SuppressWarnings({ "unchecked", "rawtypes" })
public BaseDaoImpl() {
Class<? extends IBaseDao> clz = this.getClass();
ParameterizedType pt = (ParameterizedType)
clz.getGenericSuperclass();
clazz = (Class) pt.getActualTypeArguments()[0];
}
// insert into tb_users(username,password) values(?,?)
@Override
public int save(T obj) {
StringBuilder sb1 = new StringBuilder("insert into tb_");
sb1.append(clazz.getSimpleName().toLowerCase()).append("(");
StringBuilder sb2 = new StringBuilder(") values(");
Field[] fs = clazz.getDeclaredFields();
List<Object> params = new ArrayList<>();
try {
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
f.setAccessible(true);
Object val = f.get(obj);
if (val != null) {
sb1.append(f.getName()).append(",");
sb2.append("?,");
params.add(val);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
if (sb1.toString().endsWith(","))
sb1.deleteCharAt(sb1.length() - 1);
if (sb2.toString().endsWith(","))
sb2.deleteCharAt(sb2.length() - 1);
String sql = sb1.toString() + sb2.toString() + ")";
return update(sql, params.toArray());
}
private int update(String sql, Object... params) {
int res = 0;
try {
res = JdbcUtil.executeUpdate(sql, params);
} finally {
JdbcUtil.releaseConnection();
}
return res;
}
// 按照规则:类名称---表名称 列名称---属性
public List<T> findByExample(T obj) {
List<T> res = new ArrayList<>();
try {
StringBuilder sb = new StringBuilder("select * from tb_");
sb.append(clazz.getSimpleName().toLowerCase()).append(" where
1=1 ");
// 生成sql语句
Field[] fs = clazz.getDeclaredFields();
List<Object> params = new ArrayList<>();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
f.setAccessible(true);
Object val = f.get(obj);
if (val != null) {
sb.append(" and ").append(f.getName()).append("=? ");
params.add(val);
}
}
ResultSet rs = JdbcUtil.executeQuery(sb.toString(),
params.toArray());
while (rs.next()) {
ResultSetMetaData rsmd = rs.getMetaData();
T t = clazz.newInstance(); // 调用无参构造器构建对象
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
String fieldName = rsmd.getColumnLabel(i); // 获取列名称
Field f = clazz.getDeclaredField(fieldName); // 根据列名称获取对应的Field对象
f.setAccessible(true);
f.set(t, rs.getObject(i)); // 对t对象中对应的属性进行赋值
}
res.add(t);
}
rs.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
JdbcUtil.releaseConnection();
}
return res;
}
}
获取数据表的元数据
ResultSetMetaData对象保存了所有ResultSet对象中关于字段的信息,提供了对应的方法获取字段相关 的信息
- int getColumnCount()获取ResultSet对象中的字段个数
- String getColumnName(int index)获取ResultSet对象中指定序号对应字段的名称获取表的元数据信息
ResultSet rs=ps.executeQuery();
ResultSetMetaData rsmd= rs.getMetaData();
int len=rsmd.getColumnCount();//獲取列數
for(int i=1;i<=len;i++){
String columnName=rsmd.getColumnName(i);//獲取列名稱
String typeName=rsmd.getColumnTypeName(i);//獲取列的類型名稱
System.out.println(columnName+"\t"+typeName);
}
SQLException
调用JDBC API方法时经常会有一个受检型异常/非运行时异常,必须在编码过程中针对异常进行处理。但是大部分出现的异常是不能通过编码解决,例如SQL语句语法错误、关闭Connection对象时出错。而且SQLException的颗粒度太粗,不能明确表达异常的原因。
所以常见的处理方案有2种:1、向上抛出异常或者记录报错信息。2、将SQLException转换为运行时异常,由上层调用方进行处理
XxxDAO
使用BaseDaoImpl实现具体类的增删改查
create table tb_user(
id bigint primary key auto_increment,
username varchar(20) not null unique,
password varchar(20) not null
)engine=innodb default charset utf8;
根据对应的表结构定义实体类
- 注意规则:类名称和表名称对应,属性名称和字段名称对应
- 类中的属性不要使用简单类型,例如long
- 判断的需要
- setObject
public class User implements Serializable{
private Long id;
private String username;
private String password;
}
定义对应的接口
public interface IUserDao extends IBaseDao<User>{
//如果有userDao中需要添加的特殊方法,声明在这里
}
定义对应的实现类
public class UserDaoImpl extends BaseDaoImpl<User> implements IUserDao{
//如果有特殊方法,则在这里实现,实际上通用的CRUD方法已经从BaseDao中继承
}
定义工厂类用于创建具体的实现类对象
public class DaoFactory{
public static IUserDao getUserDao(){
return new UserDaoImpl();
}
}