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真有意思(/斜眼笑)