一、什么是DAO
数据访问对象(Data Access Object),是标准 J2EE 设计模式之一。开发人员用这种模式将底层数据访问操作与高层业务逻辑分离开。
二、四要素
- 一个 DAO 工厂类
- 一个 DAO 接口
- 一个实现了 DAO 接口的具体类
- 数据传输对象(有时称为值对象 value object---> vo)
三、具体类
- DAO接口
用于声明对于数据库的操作
- VO(Value Object):
于存放一行数据即一条记录的类
- DAOImpl
必须实现DAO接口,真实实现DAO接口的函数,但是不包括数据库的打开和关闭
- DAOFactory
工厂类,含有getInstance()创建一个DAO类
四、编写DAO
- 建立数据库表
- 创建实体类
说明
- java类必须要实现java.io.Serializable接口(因为交互时需要通过流传递对象);
- 类中的字段必须使用private封装,封装好的属性必须提供get和set方法
- 类中的属性不允许使用基本数据类型,都必须使用基本数据类型的包装类。原因是基本数据类型的默认值是0,而包装类的默认数据类型是null
- 类中必须保留无参构造方法
- java类的名称尽量与表名称保持一致。表名称如果是这种TB_USER则类名称为USER
- 创建DAO的基类(接口类)
- 创建DAO的实现类
- 创建具体表的DAO类
- 创建具体表的DAO类的实现类
- 创建DAO类工厂类
DAO的编程步骤
1、创建DAO接口类 (命名:I + 功能名称 + Dao)
2、创建DAO的接口实现类 (命名:功能名称 + Dao +Impl结尾)
3、创建实体类 值对象 (即数据库中的表——>类)
4、 创建Dao的工厂类 (需要注册每个Dao类)
五、分层开发
1、特点
- 每一层都有自己的职责
- 上一层不用关心下一层的实现细节,上一层通过下一层提供的对外接口来使用其功能
- 上一层调用下一层的功能,下一层不能调用上一层功能
2、好处
- 层专注于自己功能的实现,便于提高质量
- 便于分工协作,提高开发效率
- 便于代码复用
- 便于程序扩展
六、示例代码
1、基础入门
- 建表语句()
--Mysql CREATE TABLE MOBILE_TELEPHONE( MID INT(6) AUTO_INCREMENT PRIMARY KEY, --主键自动增长 BRAND VARCHAR(50) NOT NULL, --品牌 MODEL VARCHAR(50) NOT NULL,--型号 PRICE DOUBLE(9,2) NOT NULL, --价格 COUNT INT NOT NULL, --数量 VERSION VARCHAR(50) NOT NULL,--版本 CONSTRAINT UK_MT_BRAND UNIQUE(BRAND) ) -- oracle CREATE TABLE MOBILE_TELEPHONE( MID NUMBER(6) PRIMARY KEY, --主键 BRAND VARCHAR2(50) NOT NULL, MODEL VARCHAR2(50) NOT NULL, PRICE NUMBER(9,2) NOT NULL, COUNT NUMBER(8) NOT NULL, VERSION VARCHAR(50) NOT NULL, CONSTRAINT "UK_MT_BRAND" UNIQUE(BRAND) ) --索引 主键自动增长 CREATE SEQUENCE SEQ_MT_MID --序列名 INCREMENT BY 1 -- 每次加几个 START WITH 1 -- 从1开始计数 NOMAXVALUE -- 不设置最大值 NOCYCLE -- 一直累加,不循环 NOCACHE; --不缓存
- c3p0-config.xml
20 2 2 60 3000 2 0 1000 false Test false 60 100 0 jdbc:mysql://localhost:3306/scott root root com.mysql.jdbc.Driver - DbManager
public class DbManager { private static volatile DbManager instance = null; /** * 数据库连接池核心类 */ static ComboPooledDataSource ds = null; /** * 数据库线程池配置名称 */ private static final String C3P0_CONFIG_NAME = "mysql"; static { ds = new ComboPooledDataSource(C3P0_CONFIG_NAME); } private DbManager() { } public static DbManager getInstance() { if (instance == null) { synchronized (DbManager.class) { if (instance == null) { instance = new DbManager(); } } } return instance; } public Connection getConn() { Connection connection = null; try { connection = ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } public void close(ResultSet rs, Statement statement, Connection conn) { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, statement); } } } public void close(Statement statement, Connection conn) { if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } public void close(ResultSet rs, Statement statement) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally { close(statement); } } } public void close(Statement statement) { if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 专门用于发送增删改语句的方法 * * @param * @return true表示成功 false表示失败 */ public boolean executeUpdate(String sql, Object... params) throws SQLException { int count = 0; Connection conn = getConn(); PreparedStatement ps = null; try { conn.setAutoCommit(false); ps = conn.prepareStatement(sql); setParams(ps, params); //使用Statement对象发送SQL语句 count = ps.executeUpdate(); getConn().commit(); } catch (SQLException e) { conn.rollback(); } finally { close(ps, conn); } return count != -3; } public boolean executeBatch(String sql, List
- MobileDao
1>批量插入5条测试数据 2>查询所有的数据 3>通过主键删除记录 ... 4>根据业务需求编写
2、Dao封装
(未经过严格测试,仅供学习使用,请不要用于实战开发(建议使用hibernate,mybatis框架))
(未经过严格测试,仅供学习使用,请不要用于实战开发(建议使用hibernate,mybatis框架))
(未经过严格测试,仅供学习使用,请不要用于实战开发(建议使用hibernate,mybatis框架))
- BaseDao
public interface IBaseDao { /** * 封装增删改的操作 * * @return */ public boolean executeUpdate(String sql, Object... params); /** * 查询单个对象 * * @param sql * @param entity * @param params * @param
* @return */ public T query(String sql, Class entity, Object... params); /** * 查询多个对象 * * @param sql * @param entity * @param params * @param * @return */ public List queryList(String sql, Class entity, Object... params); /** * 保存对象 * @param entity * @param */ public void save(T entity); /** * 通过主键更新对象 * @param entity * @param * @throws Exception */ public void update(T entity) throws Exception; /** * 通过主键删除对象 * @param entity * @param * @throws Exception */ public void delete(T entity) throws Exception; } - BaseDaoImpl
import com.werner.jdbc.dao.IBaseDao; import com.werner.jdbc.utils.DbManager; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * 未经过严格测试,仅供学习使用,请不要用于实战开发(建议使用hibernate,mybatis框架) */ public class BaseDaoImpl implements IBaseDao { @Override public boolean executeUpdate(String sql, Object... params) { boolean flag = false; try { //这里可能有错误,暂时没去更改,注意 flag = DbManager.getInstance().executeUpdate(sql, params); } catch (SQLException e) { e.printStackTrace(); } return flag; } @Override public
T query(String sql, Class entity, Object... params) { return queryList(sql, entity, params).get(0); } @Override public List queryList(String sql, Class entity, Object... params) { Connection conn = DbManager.getInstance().getConn(); PreparedStatement ps = null; ResultSet rs = DbManager.getInstance().execQuery(conn, ps, sql, params); List list = new ArrayList<>(); if (rs != null) { try { Field[] fields = entity.getDeclaredFields(); while (rs.next()) { T t = entity.newInstance(); list.add(t); for (Field field : fields) { field.setAccessible(true); Object value = rs.getObject(field.getName()); setValue(t, field, value); } } } catch (SQLException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } finally { DbManager.getInstance().close(rs, ps, conn); } } return list; } private void setValue(T t, Field f, Object value) throws IllegalAccessException { if (null == value) return; String valueStr = value.toString(); String typeName = f.getType().getName(); if ("java.lang.Byte".equals(typeName) || "byte".equals(typeName)) { f.set(t, Byte.parseByte(valueStr)); } else if ("java.lang.Short".equals(typeName) || "short".equals(typeName)) { f.set(t, Short.parseShort(valueStr)); } else if ("java.lang.Integer".equals(typeName) || "int".equals(typeName)) { f.set(t, Integer.parseInt(valueStr)); } else if ("java.lang.Long".equals(typeName) || "long".equals(typeName)) { f.set(t, Long.parseLong(valueStr)); } else if ("java.lang.Float".equals(typeName) || "float".equals(typeName)) { f.set(t, Float.parseFloat(valueStr)); } else if ("java.lang.Double".equals(typeName) || "double".equals(typeName)) { f.set(t, Double.parseDouble(valueStr)); } else if ("java.lang.String".equals(typeName)) { f.set(t, value.toString()); } else if ("java.lang.Character".equals(typeName) || "char".equals(typeName)) { f.set(t, value); } else if ("java.util.Date".equals(typeName)) { f.set(t, new Date(((java.sql.Date) value).getTime())); } else if ("java.util.Timer".equals(typeName)) { f.set(t, new Time(((java.sql.Time) value).getTime())); } else if ("java.sql.Timestamp".equals(typeName)) { f.set(t, value); } else if ("java.math.BigDecimal".equals(typeName)) { f.set(t, value); } else { System.out.println("Sql:不支持的数据类型"); } } /** * 保存 */ public void save(T entity) { PreparedStatement ps = null; Connection conn = DbManager.getInstance().getConn(); //SQL语句,insert into table name ( String sql = "insert into " + entity.getClass().getSimpleName().toLowerCase() + "("; //获得带有字符串get的所有方法的对象 List list = this.matchPojoMethods(entity, "get"); Iterator iter = list.iterator(); //拼接字段顺序 insert into table emp(empno,ename,job, while (iter.hasNext()) { Method method = iter.next(); sql += method.getName().substring(3).toLowerCase() + ","; } //去掉最后一个,符号insert insert into table name(empno,ename,job) values( sql = sql.substring(0, sql.lastIndexOf(",")) + ") values("; //拼装预编译SQL语句insert insert into table name(empno,name,email) values(?,?,?, for (int j = 0; j < list.size(); j++) { sql += "?,"; } //去掉SQL语句最后一个,符号insert insert into table name(empno,ename,job) values(?,?,?); sql = sql.substring(0, sql.lastIndexOf(",")) + ")"; //到此SQL语句拼接完成,打印SQL语句 System.out.println(sql); //获得预编译对象的引用 try { ps = DbManager.getInstance().getConn().prepareStatement(sql); int i = 0; //把指向迭代器最后一行的指针移到第一行. iter = list.iterator(); while (iter.hasNext()) { Method method = iter.next(); //此初判断返回值的类型,因为存入数据库时有的字段值格式需要改变,比如String,SQL语句是'aaa' if (method.getReturnType().getSimpleName().indexOf("String") != -1) { ps.setString(++i, this.getString(method, entity)); } else if (method.getReturnType().getSimpleName().indexOf("Date") != -1) { ps.setDate(++i, this.getDate(method, entity)); } else { ps.setInt(++i, this.getInt(method, entity)); } } ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { DbManager.getInstance().close(ps, conn); } } /** * 修改 */ public void update(T entity) throws Exception { String sql = "update " + entity.getClass().getSimpleName().toLowerCase() + " set "; //获得该类所有get方法对象集合 List list = this.matchPojoMethods(entity, "get"); //临时Method对象,负责迭代时装method对象. Method tempMethod = null; //由于修改时不需要修改ID,所以按顺序加参数则应该把Id移到最后. Method idMethod = null; Iterator iter = list.iterator(); while (iter.hasNext()) { tempMethod = iter.next(); //如果方法名中带有ID字符串并且长度为2,则视为ID. if (tempMethod.getName().lastIndexOf("Id") != -1 && tempMethod.getName().substring(3).length() == 2) { //把ID字段的对象存放到一个变量中,然后在集合中删掉. idMethod = tempMethod; iter.remove(); //如果方法名去掉set/get字符串以后与pojo + "id"想符合(大小写不敏感),则视为ID } else if ((entity.getClass().getSimpleName() + "Id").equalsIgnoreCase(tempMethod.getName().substring(3))) { idMethod = tempMethod; iter.remove(); } } //把迭代指针移到第一位 iter = list.iterator(); while (iter.hasNext()) { tempMethod = iter.next(); sql += tempMethod.getName().substring(3).toLowerCase() + "= ?,"; } //去掉最后一个,符号 sql = sql.substring(0, sql.lastIndexOf(",")); //添加条件 sql += " where " + idMethod.getName().substring(3).toLowerCase() + " = ?"; //SQL拼接完成,打印SQL语句 System.out.println(sql); PreparedStatement statement = DbManager.getInstance().getConn().prepareStatement(sql); int i = 0; iter = list.iterator(); while (iter.hasNext()) { Method method = iter.next(); //此初判断返回值的类型,因为存入数据库时有的字段值格式需要改变,比如String,SQL语句是'aaa' if (method.getReturnType().getSimpleName().indexOf("String") != -1) { statement.setString(++i, this.getString(method, entity)); } else if (method.getReturnType().getSimpleName().indexOf("Date") != -1) { statement.setDate(++i, this.getDate(method, entity)); } else { statement.setInt(++i, this.getInt(method, entity)); } } //为Id字段添加值 if (idMethod.getReturnType().getSimpleName().indexOf("String") != -1) { statement.setString(++i, this.getString(idMethod, entity)); } else { statement.setInt(++i, this.getInt(idMethod, entity)); } //执行SQL语句 statement.executeUpdate(); //关闭预编译对象 statement.close(); //关闭连接 conn.close(); } /** * 删除 */ public void delete(T entity) throws Exception { String sql = "delete from " + entity.getClass().getSimpleName().toLowerCase() + " where "; //存放字符串为主键的字段对象 Method idMethod = null; //取得字符串为"id"的字段对象 List list = this.matchPojoMethods(entity, "get"); Iterator iter = list.iterator(); while (iter.hasNext()) { Method tempMethod = iter.next(); //如果方法名中带有ID字符串并且长度为2,则视为ID. if (tempMethod.getName().lastIndexOf("Id") != -1 && tempMethod.getName().substring(3).length() == 2) { //把ID字段的对象存放到一个变量中,然后在集合中删掉. idMethod = tempMethod; iter.remove(); //如果方法名去掉set/get字符串以后与pojo + "id"想符合(大小写不敏感),则视为ID } else if ((entity.getClass().getSimpleName() + "Id").equalsIgnoreCase(tempMethod.getName().substring(3))) { idMethod = tempMethod; iter.remove(); } } sql += idMethod.getName().substring(3).toLowerCase() + " = ?"; PreparedStatement statement = DbManager.getInstance().getConn().prepareStatement(sql); //为Id字段添加值 int i = 0; if (idMethod.getReturnType().getSimpleName().indexOf("String") != -1) { statement.setString(++i, this.getString(idMethod, entity)); } else { statement.setInt(++i, this.getInt(idMethod, entity)); } } /** * 过滤当前Pojo类所有带传入字符串的Method对象,返回List集合. */ private List matchPojoMethods(T entity, String methodName) { //获得当前Pojo所有方法对象 Method[] methods = entity.getClass().getDeclaredMethods(); //List容器存放所有带get字符串的Method对象 List list = new ArrayList (); //过滤当前Pojo类所有带get字符串的Method对象,存入List容器 for (int index = 0; index < methods.length; index++) { if (methods[index].getName().indexOf(methodName) != -1) { list.add(methods[index]); } } return list; } /** * 方法返回类型为int或Integer类型时,返回的SQL语句值.对应get */ private Integer getInt(Method method, T entity) throws Exception { return (Integer) method.invoke(entity, new Object[]{}); } /** * 方法返回类型为String时,返回的SQL语句拼装值.比如'abc',对应get */ private String getString(Method method, T entity) throws Exception { return (String) method.invoke(entity, new Object[]{}); } /** * 方法返回类型为Date时,返回的SQL语句拼装值,对应get */ private Date getDate(Method method, T entity) throws Exception { return (Date) method.invoke(entity, new Object[]{}); } /** * 参数类型为Integer或int时,为entity字段设置参数,对应set */ private Integer setInt(Method method, T entity, Integer arg) throws Exception { return (Integer) method.invoke(entity, new Object[]{arg}); } /** * 参数类型为String时,为entity字段设置参数,对应set */ private String setString(Method method, T entity, String arg) throws Exception { return (String) method.invoke(entity, new Object[]{arg}); } /** * 参数类型为Date时,为entity字段设置参数,对应set */ private Date setDate(Method method, T entity, Date arg) throws Exception { return (Date) method.invoke(entity, new Object[]{arg}); } }