DAO的重构
1.PreparedStatement(预编译处理语句)
Statement接口只能实现静态SQL语句,我们使用PreparedStatement。
PreparedStatement是Statement子接口,表示预编译语句对象,通过占位符?来拼接SQL
创建预编译语句对象
@Test
public void testSaveByPreparedStatement() throws Exception{
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
Connection conn = JdbcUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符参数值
ps.setString(1,"Dans");
ps.setString(2,"9999999");
ps.setString(3,"F");
ps.setInt(4,25);
ps.executeUpdate();//注意,没有参数
JdbcUtil.close(conn,ps,null);
}
具体实现DAO的代码:
public class StudentDAOImpl implements IStudentDAO {
@Override
public void save(Student stu) {
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,stu.getName());
ps.setString(2,stu.getId());
ps.setString(3,stu.getSex());
ps.setInt(4,stu.getAge());
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
@Override
public void delete(String id) {
String sql = "DELETE FROM student WHERE Student_id = ?";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,id);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
@Override
public void update(String id, Student newStu) {
String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,newStu.getName());
ps.setString(2,newStu.getSex());
ps.setInt(3,newStu.getAge());
ps.setString(4,id);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
@Override
public Student get(String id) {
String sql = "SELECT * FROM student WHERE Student_id = ?";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,id);
rs = ps.executeQuery();
if (rs.next()) {
Student stu = new Student();
stu.setName(rs.getString("Student_name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
stu.setId(id);
return stu;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,rs);
}
return null;
}
@Override
public List<Student> listAll() {
List<Student> list = new ArrayList<>();
String sql = "SELECT * FROM student";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
Student stu = new Student();
stu.setName(rs.getString("Student_name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
stu.setId(rs.getString("Student_id"));
list.add(stu);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,rs);
}
return list;
}
}
PreparedStatement原理
用预编译处理语句在预编译池中已经存在了相同的代码,那么1-3步可以省略不写,因此性能会更高。
但是MySQL不支持PreparedStatement,Oracle支持PreparedStatement.
防止SQL注入问题
如果我们使用Statement方式来进行DQL操作,
String sql = "SELECT * FROM student WHERE Student_name = 'admin' AND password = '1234'";
当我把填入的admin换成:' OR 1=1 OR '
即如下:
String sql = "SELECT * FROM student WHERE Student_name = '' OR 1=1 OR '' AND password = '1234'";
只要数据库中存在数据,那么查询就是成功的,这便是SQL注入。
而使用PrepareStatement便可以解决SQL注入问题。
String sql = "SELECT * FROM student WHERE Student_name = ? AND password = ?";
2.事务
我们用转账的例子来进行模拟。
试想,我们在转账的时候,从我的账户转出1000元,然后服务器收到消息,再将收款方账户增加1000元,如果这两件事是独立的,那么若在我转出的同时,服务器停止工作(断电或者被黑了),那么收款方将无法收到转账消息,但是我的账户已经转出了1000元,那么我将白白损失1000元。
@Test
public void test1() throws Exception {
Connection conn = JdbcUtil.getConnection();
//--------------检查Bobbui的账户余额-------------------
String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "Bobbui");
ps.setInt(2, 1000);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
throw new RuntimeException("余额不足!");
}
//---------------减少Bobbui账户1000元------------------
sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "Bobbui");
ps.executeUpdate();
//使用异常模拟停电
int a = 1 / 0;
//--------------增加DANS1000元-------------------------
sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "DANS");
ps.executeUpdate();
JdbcUtil.close(conn, ps, rs);
}
因此我们引出事务(Transaction),事务的相关操作:
@Test
public void test2() {
Connection conn = JdbcUtil.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn.setAutoCommit(false);
//--------------检查Bobbui的账户余额-------------------
String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "Bobbui");
ps.setInt(2, 1000);
rs = ps.executeQuery();
if (!rs.next()) {
throw new RuntimeException("余额不足!");
}
//---------------减少Bobbui账户1000元------------------
sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "Bobbui");
ps.executeUpdate();
//使用异常模拟停电
//int a = 1 / 0;
//--------------增加DANS1000元-------------------------
sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "DANS");
ps.executeUpdate();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
conn.rollback();
} catch (Exception v) {
v.printStackTrace();
}
} finally {
JdbcUtil.close(conn, ps, rs);
}
}
这样就解决了上述问题。
3.批处理操作
@Test
public void testSaveByPreparedStatement_batch() throws Exception{
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
Connection conn = JdbcUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < 3000; i++) {
ps.setString(1,"Dans");
ps.setString(2,"9999999");
ps.setString(3,"F");
ps.setInt(4,25);
ps.addBatch();//添加进批处理中
if (i % 200 == 0) {
ps.executeBatch();//执行批量操作
ps.clearBatch();//清除缓存
ps.clearParameters();//清除参数
}
}
}
但是,MySQL服务器既不支持PrepareStatement的性能优化,也不支持JDBC中的批量操作,但是在新的JDBC驱动中,我们可以通过设置参数来进行优化:在URL后面缀上?rewriteBatchedStatements=true(在5.1.13版本后都支持)
url=jdbc:mysql:///studentinfo?rewriteBatchedStatements=true
4.BOLB和TEXT类型
@Test
public void test1() throws Exception {
String sql = "INSERT INTO image (img) VALUES (?)";
Connection conn = JdbcUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setBlob(1,new FileInputStream("D:/1.jpg"));
ps.executeUpdate();
JdbcUtil.close(conn,ps,null);
}
@Test
public void test2() throws Exception {
String sql = "SELECT * FROM image WHERE id = ?";
Connection conn = JdbcUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,1);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob("img");
InputStream in = blob.getBinaryStream();
Files.copy(in, Paths.get("D:/1234.jpg"));
}
JdbcUtil.close(conn,ps,null);
}
5.获取自动生成的主键
@Test
public void testStatement() throws Exception {
String sql = "INSERT INTO pk (name,age) VALUES (?,?)";
Connection conn = JdbcUtil.getConnection();
//设置主键可获取
PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
ps.setString(1,"ZZZ");
ps.setInt(2,20);
ps.executeUpdate();
//获取主键结果集
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
Integer id = rs.getInt(1);
System.out.println(id);
}
JdbcUtil.close(conn,ps,rs);
}
很简单,没什么好说的。
6.连接池
创建DataSource对象
使用DataSource需要导入commos-dbcp.jar和commons-pool.jar两个jar包
/**
* 使用DBCP连接池
*/
public class DBCPTest {
/**
* 创建一个连接池对象
* @return DataSource
*/
public DataSource getDataSource() {
//创建连接对象
BasicDataSource ds = new BasicDataSource();
//设置连接数据库的四要素
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql:///studentinfo");
ds.setUsername("root");
ds.setPassword("password");
ds.setMaxWaitMillis(1000);//设置最大等待时间
ds.setInitialSize(3);//设置初始连接数
ds.setMaxIdle(3);//最大空闲连接数
ds.setMinIdle(1);//最小空闲连接数
ds.setMaxTotal(10);//最大连接数
return ds;
}
@Test
public void test1() throws Exception {
DataSource ds = getDataSource();
Connection conn = ds.getConnection();
String sql = "SELECT * FROM student";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("Student_id"));
}
}
}
使用properties文件解耦DBCP
public class DBCPUtil {
private static DataSource ds = null;
static {
try {
Properties p = new Properties();
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties"));
ds = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
//从连接池中获取Connection对象
return ds.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//释放资源
public static void close(Connection conn, Statement st, ResultSet rs) {
try {
if (st != null) {
st.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
使用BasicDataSourceFactory很快获得properties文件中的值
@Test
public void test1() throws Exception {
//DataSource ds = getDataSource();
//Connection conn = ds.getConnection();
Connection conn = DBCPUtil.getConnection();
String sql = "SELECT * FROM student";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("Student_id"));
}
}
7.Druid连接池
需要从阿里巴巴下载Druid的jar包
各数据库性能对比:
使用起来那就很简单了
static {
try {
Properties p = new Properties();
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties"));
//基于DBCP
//ds = BasicDataSourceFactory.createDataSource(p);
//基于Druid
ds = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
8.JDBC Template
我们观察DML的增删改操作,发现他们工作的流程有异曲同工之处:
@Override
public void save(Student stu) {
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,stu.getName());
ps.setString(2,stu.getId());
ps.setString(3,stu.getSex());
ps.setInt(4,stu.getAge());
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
@Override
public void delete(String id) {
String sql = "DELETE FROM student WHERE Student_id = ?";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,id);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
他们的工作原理都是一样的,我们考虑将他们相同的部分抽取出来,重构出一个Template类,然后用循环来设置占位符参数
/**
* DML操作(增删改)模板
*
* @param sql DML造作的SQL模板
* @param params SQL模板中?对应参数的值
* @return 受影响的行数
*/
public static int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
//设置占位符参数
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, ps, null);
}
return 0;
}
实现类修改为:
@Override
public void save(Student stu) {
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
JdbcTemplate.update(sql, params);
}
@Override
public void delete(String id) {
JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
}
@Override
public void update(String id, Student newStu) {
String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
JdbcTemplate.update(sql, params);
}
对于DQL操作来说,也是有很多相同的地方,将其抽取出来,形成DQL模板
@Override
public Student get(String id) {
List<Student> list = new ArrayList<>();
String sql = "SELECT * FROM student WHERE Student_id = ?";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,id);
rs = ps.executeQuery();
while (rs.next()) {
Student stu = new Student();
stu.setName(rs.getString("Student_name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
stu.setId(id);
list.add(stu);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,rs);
}
return list.size() == 1 ? list.get(0) : null;
}
public static List<Student> query(String sql, Object... params) {
List<Student> list = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
//处理结果集
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, ps, rs);
}
return list;
}
实现类修改为:
@Override
public Student get(String id) {
String sql = "SELECT * FROM student WHERE Student_id = ?";
List<Student> list = JdbcTemplate.query(sql,id);
return list.size() == 1 ? list.get(0) : null;
}
9.Template优化
上述的Template中,我们只能将数据库的结果集每一行封装成固定的Student对象,在开发中我们需要操作N张表,便有N个对象,上述方法显然是不合理的。我们将模板类命名为JDBCTemplate,那么该类应该适用于任何情况。
因此,我们考虑将处理结果集的行为交给每个对象的DAO实现类来做。
为了避免不同的DAO实现类心意的处理结果集的方法名字不同,我们用一个接口来指定规范:
//结果集处理器,规范处理结果集的方法名称
public interface IResultSetHandler {
//处理结果集
List handle(ResultSet rs) throws Exception;
}
但是我们考虑到,不一定每一个结果集都需要用List来存,所以这里我们用泛型:
public interface IResultSetHandler<T> {
T handle(ResultSet rs) throws Exception;
}
把结果集每一行数据封装成Student对象的实现:
class StudentResultSetHandle implements IResultSetHandle<List<Student>> {
@Override
public List<Student> handle(ResultSet rs) throws Exception {
List list = new ArrayList();
while (rs.next()) {
Student stu = new Student();
stu.setName(rs.getString("Student_name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
stu.setId(rs.getString("Student_id"));
list.add(stu);
}
return list;
}
}
但是我们发现,若存在N个DAO实现类,那么便会有N个实现Handler类,而这些类的结构完全相同:
- 创建一个对象
- 取出结果集中当前光标所在行的某一列数据
- 调用该对象的setter方法,把某一列的数据设置进去(可以使用Java的内省机制)
因此可以将其抽取出来形成结果集处理器,但是要注意:
- 规定表中的列名必须和对象中的属性名相同
- 规定表中的列名的类型必须和Java中的类型要匹配
结果集处理器BeanHandler代码如下:
/**
* new BeanHandle(Student.class)
* new BeanHandle(Teacher.class)
*
* @param <T>
*/
//表示把结果集中的一行数据封装成一个对象,专门针对结果集中只有一行数据的情况
public class BeanHandler<T> implements IResultSetHandler<T> {
private Class<T> classType;//把结果集中的一行数据封装成什么类型的对象
public BeanHandler(Class<T> classType) {
this.classType = classType;
}
/**
* 规定表中的列名称必须和对象中的属性名相同;
* 规定表中的列名的类型必须和Java中的类型要匹配
* --------------##使用内省机制##--------------
*
* @param rs
* @return
* @throws Exception
*/
@Override
public T handle(ResultSet rs) throws Exception {
//1.创建对应类的一个对象
T obj = classType.newInstance();
//2.取出结果集中当前光宝所在行的某一列的数据
BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
if (rs.next()) {
for (PropertyDescriptor pd : pds) {
String columnName = pd.getName();//获取对象的属性名,属性和列名相同
Object val = rs.getObject(columnName);
//System.out.println(columnName + "," + val);
//3.调用该对象的setter方法,把某一列的数据设置进去
pd.getWriteMethod().invoke(obj,val);
}
}
return obj;
}
}
BeanListHandler同理如下:
public class BeanListHandler<T> implements IResultSetHandler<List<T>> {
private Class<T> classType;//把结果集中的一行数据封装成什么类型的对象
public BeanListHandler(Class<T> classType) {
this.classType = classType;
}
@Override
public List<T> handle(ResultSet rs) throws Exception {
List<T> list = new ArrayList<>();
while (rs.next()) {
T obj = classType.newInstance();
list.add(obj);
BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
String columnName = pd.getName();//获取对象的属性名,属性和列名相同
Object val = rs.getObject(columnName);
pd.getWriteMethod().invoke(obj,val);
}
}
return list;
}
}
此时JdbcTemplate模板类如下:
//JDBC操作模板类
public class JdbcTemplate {
/**
* DQL(查询)操作模板
* 如果查询多个学生,返回一个List<Student>
* 单个学生也可以返回一个List<Student>
*
* @param sql DQL操作的SQL模板
* @param params SQL中?对应的参数值
* @return List集合
*/
public static <T>T query(String sql, IResultSetHandler<T> rsh, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
rs = ps.executeQuery();
return rsh.handle(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, ps, rs);
}
throw new RuntimeException("查询错误");
}
/**
* DML操作(增删改)模板
*
* @param sql DML造作的SQL模板
* @param params SQL模板中?对应参数的值
* @return 受影响的行数
*/
public static int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
//设置占位符参数
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, ps, null);
}
return 0;
}
}
实现类如下:
public class StudentDAOImpl implements IStudentDAO {
@Override
public void save(Student stu) {
String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
JdbcTemplate.update(sql, params);
}
@Override
public void delete(String id) {
JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
}
@Override
public void update(String id, Student newStu) {
String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
JdbcTemplate.update(sql, params);
}
@Override
public Student get(String id) {
String sql = "SELECT * FROM student WHERE Student_id = ?";
return JdbcTemplate.query(sql,new BeanHandler<>(Student.class), id);
}
@Override
public List<Student> listAll() {
return JdbcTemplate.query("SELECT * FROM student",new BeanListHandler<>(Student.class));
}
}
显得非常简洁,从原来的将近两百行优化到了四十行。
这些就是数据库操作的一些模板,若给他人使用,我们可以将其打成jar包,可以应付绝大部分的数据库操作。但是还是有一些小问题,我的处理结果集不一定是某个类的对象,可以是某一个值,比如我使用了数据库的聚合函数。那么我们可以用一个匿名内部类来重写IResultSetHandler接口:
@Test
public void testGetCount() {
String sql = "SELECT COUNT(Student_id) FROM student";
int totalCount = JdbcTemplate.query(sql, new IResultSetHandler<Integer>() {
@Override
public Integer handle(ResultSet rs) throws Exception {
if (rs.next()) {
return rs.getInt(1);
}
return 0;
}
});
System.out.println(totalCount);
}
当然可以使用lambda表达式:
@Test
public void testGetCount() {
String sql = "SELECT COUNT(Student_id) FROM student";
int totalCount = JdbcTemplate.query(sql, rs -> {
if (rs.next()) {
return rs.getInt(1);
}
return 0;
});
System.out.println(totalCount);
}
简洁明了。
整个DAO就是一个封装的思想,目的就是实现代码的高内聚和低耦合,便于后期维护。我们抽象出来的模板类和Handler类,就是把数据库操作中相同的或者类似的部分封装起来,然后只修改SQL语句,实现对数据库的操作,这样操作人员只需要修改Impl实现类就可以了。
Java真有意思(/斜眼笑)