預(yù)編譯sql處理(防止sql注入)
- Statement : 執(zhí)行SQL命令
- CallableStatement : 執(zhí)行存儲(chǔ)過程
- PreparedStatement : 預(yù)編譯SQL語(yǔ)句執(zhí)行
- 使用預(yù)編譯SQL語(yǔ)句的命令對(duì)象的好處 : 避免頻繁sql拼接 (可以使用占位符), 可以防止sql注入
- 例如: 登陸模塊輸入用戶名,密碼的時(shí)候, 可以避免用戶輸入的惡意密碼
public class App {
// 連接參數(shù)
//private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
private String url = "jdbc:mysql:///jdbc_demo";
private String user = "root";
private String password = "root";
private Connection con;
private Statement stmt;
private PreparedStatement pstmt;
private ResultSet rs;
// 1. 沒有使用防止sql注入的案例
@Test
public void testLogin() {
// 1.0 模擬登陸的用戶名缤底,密碼
String userName = "tom";
//String pwd = "8881";
String pwd = " ' or 1=1 -- ";
// SQL語(yǔ)句
String sql = "select * from admin where userName='"+userName+"' and pwd='"+pwd+"' ";
System.out.println(sql);
try {
// 1.1 加載驅(qū)動(dòng)汰寓,創(chuàng)建連接
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);
// 1.2 創(chuàng)建stmt對(duì)象
stmt = con.createStatement();
// 1.3 執(zhí)行查詢
rs = stmt.executeQuery(sql);
// 業(yè)務(wù)判斷
if (rs.next()) {
System.out.println("登陸成功, 編號(hào):" + rs.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 1.4 關(guān)閉
try {
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 2. 使用PreparedStatement, 防止sql注入
@Test
public void testLogin2() {
// 1.0 模擬登陸的用戶名莹菱,密碼
String userName = "tom";
//String pwd = "8881";
String pwd = " ' or 1=1 -- ";
// SQL語(yǔ)句
String sql = "select * from admin where userName=? and pwd=? ";
try {
// 1.1 加載驅(qū)動(dòng),創(chuàng)建連接
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);
// 1.2 創(chuàng)建pstmt對(duì)象
pstmt = con.prepareStatement(sql); // 對(duì)sql語(yǔ)句預(yù)編譯
// 設(shè)置占位符值
pstmt.setString(1, userName);
pstmt.setString(2, pwd);
// 1.3 執(zhí)行
rs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("登陸成功," + rs.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 1.4 關(guān)閉
try {
rs.close();
pstmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
存儲(chǔ)過程調(diào)用
-- 存儲(chǔ)過程
-- 定義分隔符
DELIMITER $$
CREATE PROCEDURE proc_login()
BEGIN
SELECT * FROM admin;
END $$
-- 調(diào)用
CALL proc_login;
public class App_call {
// 全局參數(shù)
private Connection con;
private Statement stmt;
private PreparedStatement pstmt;
private CallableStatement cstmt; // 存儲(chǔ)過程
private ResultSet rs;
// 程序中調(diào)用存儲(chǔ)過程
@Test
public void testCall() throws Exception {
try {
//1 . 創(chuàng)建連接
con = JdbcUtil.getConnection();
//2. 創(chuàng)建執(zhí)行存儲(chǔ)過程的stmt對(duì)象
CallableStatement cstmt = con.prepareCall("CALL proc_login");
//3. 執(zhí)行(存儲(chǔ)過程)
rs = cstmt.executeQuery();
// 遍歷結(jié)果棋弥,測(cè)試
if (rs.next()) {
String name = rs.getString("userName");
String pwd = rs.getString("pwd");
// 測(cè)試
System.out.println(name + pwd);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
批處理
- 業(yè)務(wù)場(chǎng)景:當(dāng)需要向數(shù)據(jù)庫(kù)發(fā)送一批SQL語(yǔ)句執(zhí)行時(shí),應(yīng)避免向數(shù)據(jù)庫(kù)一條條的發(fā)送執(zhí)行诚欠,而應(yīng)采用JDBC的批處理機(jī)制顽染,以提升執(zhí)行效率漾岳。
- 執(zhí)行批處理SQL語(yǔ)句:
- executeBatch()方法:執(zhí)行批處理命令
- clearBatch()方法:清除批處理命令
- 實(shí)現(xiàn)批處理有兩種方式,
- 第一種方式:Statement.addBatch(sql) list
- 優(yōu)點(diǎn):可以向數(shù)據(jù)庫(kù)發(fā)送多條不同的SQL語(yǔ)句粉寞。
- 缺點(diǎn):SQL語(yǔ)句沒有預(yù)編譯; 當(dāng)向數(shù)據(jù)庫(kù)發(fā)送多條語(yǔ)句相同尼荆,但僅參數(shù)不同的SQL語(yǔ)句時(shí),需重復(fù)寫上很多條SQL語(yǔ)句唧垦。例如:
Insert into user(name,password) values(‘a(chǎn)a’,’111’);
Insert into user(name,password) values(‘bb’,’222’);
Insert into user(name,password) values(‘cc’,’333’);
Insert into user(name,password) values(‘dd’,’444’);
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 = "insert into user(name,password,email,birthday)
values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1); //把SQL語(yǔ)句加入到批命令中
st.addBatch(sql2); //把SQL語(yǔ)句加入到批命令中
st.executeBatch();
} finally {
JdbcUtil.free(conn, st, rs);
}
- 第二種方式:采用Statement.addBatch(sql)方式實(shí)現(xiàn)批處理:
- 優(yōu)點(diǎn):發(fā)送的是預(yù)編譯后的SQL語(yǔ)句捅儒,執(zhí)行效率高
- 缺點(diǎn):只能應(yīng)用在SQL語(yǔ)句相同,但參數(shù)不同的批處理中振亮。因此此種形式的批處理經(jīng)常用于在同一個(gè)表中批量插入數(shù)據(jù)巧还,或批量更新表的數(shù)據(jù)
conn = JdbcUtil.*getConnection*();
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(sql);
for(int i=0;i<50000;i++){
st.setString(1, "aaa" + i);
st.setString(2, "123" + i);
st.setString(3, "aaa" + i + "@sina.com");
st.setDate(4,**new** Date(1980, 10, 10));
st.addBatch();
if (i%1000==0){
st.executeBatch();
st.clearBatch();
}
}
st.executeBatch();
public class App {
// 測(cè)試批處理操作
@Test
public void testBatch() throws Exception {
// 模擬數(shù)據(jù)
List<Admin> list = new ArrayList<Admin>();
for (int i=1; i<21; i++) {
Admin admin = new Admin();
admin.setUserName("Jack" + i);
admin.setPwd("888" + i);
list.add(admin);
}
// 保存
AdminDao dao = new AdminDao();
dao.save(list);
}
}
// 封裝所有的與數(shù)據(jù)庫(kù)的操作
public class AdminDao {
// 全局參數(shù)
private Connection con;
private PreparedStatement pstmt;
private ResultSet rs;
// 批量保存管理員
public void save(List<Admin> list) {
// SQL
String sql = "INSERT INTO admin(userName,pwd) values(?,?)";
try {
// 獲取連接
con = JdbcUtil.getConnection();
// 創(chuàng)建stmt
pstmt = con.prepareStatement(sql); // 【預(yù)編譯SQL語(yǔ)句】
for (int i=0; i<list.size(); i++) {
Admin admin = list.get(i);
// 設(shè)置參數(shù)
pstmt.setString(1, admin.getUserName());
pstmt.setString(2, admin.getPwd());
// 添加批處理
pstmt.addBatch(); // 【不需要傳入SQL】
// 測(cè)試:每5條執(zhí)行一次批處理
if (i % 5 == 0) {
// 批量執(zhí)行
pstmt.executeBatch();
// 清空批處理
pstmt.clearBatch();
}
}
// 批量執(zhí)行
pstmt.executeBatch();
// 清空批處理
pstmt.clearBatch();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, rs);
}
}
}
插入數(shù)據(jù),獲取自增長(zhǎng)值
- 需求 :
李俊杰 : 18, 張相 : 19
- 如何設(shè)計(jì)數(shù)據(jù)庫(kù)坊秸?
編號(hào) 員工姓名 年齡 部門
01 李俊杰 18 開發(fā)部
02 張三 19 開發(fā)部
- 思考 : 如何減少數(shù)據(jù)冗余 -> 設(shè)置外鍵約束(部門與員工, 一對(duì)多的關(guān)系)
編號(hào) 員工姓名 年齡 部門
01 李俊杰 18 1
02 張三 19 1
部門編號(hào) 部門名稱
1 開發(fā)部
- 設(shè)計(jì)數(shù)據(jù)庫(kù):
員工表 (外鍵表) 【員工表有一個(gè)外鍵字段麸祷,引用了部門表的主鍵】
部門表(主鍵表)
- 編碼總體思路 : 保存員工及其對(duì)應(yīng)的部門
- 步驟:先保存部門; 再得到部門主鍵,再保存員工
- 開發(fā)具體步驟:
- 設(shè)計(jì)javabean
- 設(shè)計(jì)dao
- 測(cè)試
部門
CREATE TABLE dept(
deptId INT PRIMARY KEY AUTO_INCREMENT,
deptName VARCHAR(20)
);
-- 員工
CREATE TABLE employee(
empId INT PRIMARY KEY AUTO_INCREMENT,
empName VARCHAR(20),
dept_id INT -- 外鍵字段
);
-- 給員工表添加外鍵約束
ALTER TABLE employee ADD CONSTRAINT FK_employee_dept_deptId
FOREIGN KEY(dept_id) REFERENCES dept(deptId) ;
public class EmpDao {
private Connection con;
private PreparedStatement pstmt;
private ResultSet rs;
// 保存員工妇斤,同時(shí)保存關(guān)聯(lián)的部門
public void save(Employee emp){
// 保存部門
String sql_dept = "insert into dept(deptName) values(?)";
// 保存員工
String sql_emp = "INSERT INTO employee (empName,dept_id) VALUES (?,?)";
// 部門id
int deptId = 0;
try {
// 連接
con = JdbcUtil.getConnection();
/*****保存部門摇锋,獲取自增長(zhǎng)*******/
// 【一、需要指定返回自增長(zhǎng)標(biāo)記】
pstmt = con.prepareStatement(sql_dept,Statement.RETURN_GENERATED_KEYS);
// 設(shè)置參數(shù)
pstmt.setString(1, emp.getDept().getDeptName());
// 執(zhí)行
pstmt.executeUpdate();
// 【二站超、獲取上面保存的部門子增長(zhǎng)的主鍵】
rs = pstmt.getGeneratedKeys();
// 得到返回的自增長(zhǎng)字段
if (rs.next()) {
deptId = rs.getInt(1);
}
/*****保存員工*********/
pstmt = con.prepareStatement(sql_emp);
// 設(shè)置參數(shù)
pstmt.setString(1, emp.getEmpName());
pstmt.setInt(2, deptId);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, rs);
}
}
}
事務(wù)
- 基本概念 : 事務(wù)使指一組最小邏輯操作單元荸恕,里面有多個(gè)操作組成。 組成事務(wù)的每一部分必須要同時(shí)提交成功死相,如果有一個(gè)操作失敗融求,整個(gè)操作就回滾
- 事務(wù)ACID特性 :
- 原子性(Atomicity) : 原子性是指事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生算撮,要么都不發(fā)生
- 一致性(Consistency) : 事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另外一個(gè)一致性狀態(tài)
- 隔離性(Isolation) : 事務(wù)的隔離性是多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫(kù)時(shí)生宛,數(shù)據(jù)庫(kù)為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作數(shù)據(jù)所干擾肮柜,多個(gè)并發(fā)事務(wù)之間要相互隔離
- 持久性(Durability) : 持久性是指一個(gè)事務(wù)一旦被提交陷舅,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久性的,接下來(lái)即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響
- 事務(wù)的特性:
- 原子性审洞,是一個(gè)最小邏輯操作單元
- 一致性莱睁,事務(wù)過程中,數(shù)據(jù)處于一致狀態(tài)
- 持久性芒澜, 事務(wù)一旦提交成功仰剿,對(duì)數(shù)據(jù)的更改會(huì)反映到數(shù)據(jù)庫(kù)中
- 隔離性, 事務(wù)與事務(wù)之間是隔離的
- 案例 :
- 需求 : 張三給李四轉(zhuǎn)賬
- 設(shè)計(jì) : 賬戶表
- 技術(shù) :
- Connection :
-
void setAutoCommit(boolean autoCommit)
: 設(shè)置事務(wù)是否自動(dòng)提交; 如果設(shè)置為false痴晦,表示手動(dòng)提交事務(wù)
-
void commit()
: 手動(dòng)提交事務(wù)
-
void rollback()
: 回滾 (出現(xiàn)異常時(shí)候南吮,所有已經(jīng)執(zhí)行成功的代碼需要回退到事務(wù)開始前的狀態(tài))
Savepoint setSavepoint(String name)
- 代碼示例 :
-- 賬戶表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
accountName VARCHAR(20),
money DOUBLE
);
-- 轉(zhuǎn)賬
UPDATE account SET money=money-1000 WHERE accountName='張三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
public class AccountDao {
// 全局參數(shù)
private Connection con;
private PreparedStatement pstmt;
// 1. 轉(zhuǎn)賬,沒有使用事務(wù)
public void trans1() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
try {
con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
con.setAutoCommit(true);
/*** 第一次執(zhí)行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次執(zhí)行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
// 2. 轉(zhuǎn)賬誊酌,使用事務(wù)
public void trans2() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";
try {
con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
// 一部凑、設(shè)置事務(wù)為手動(dòng)提交
con.setAutoCommit(false);
/*** 第一次執(zhí)行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次執(zhí)行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 二露乏、 出現(xiàn)異常,需要回滾事務(wù)
con.rollback();
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 三砚尽、所有的操作執(zhí)行成功, 提交事務(wù)
con.commit();
JdbcUtil.closeAll(con, pstmt, null);
} catch (SQLException e) {
}
}
}
// 3. 轉(zhuǎn)賬施无,使用事務(wù), 回滾到指定的代碼段
public void trans() {
// 定義個(gè)標(biāo)記
Savepoint sp = null;
// 第一次轉(zhuǎn)賬
String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
// 第二次轉(zhuǎn)賬
String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='張三';";
String sql_ls2 = "UPDATE1 account SET money=money+500 WHERE accountName='李四';";
try {
con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
con.setAutoCommit(false); // 設(shè)置事務(wù)手動(dòng)提交
/*** 第一次轉(zhuǎn)賬 ***/
pstmt = con.prepareStatement(sql_zs1);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls1);
pstmt.executeUpdate();
// 回滾到這個(gè)位置必孤?
sp = con.setSavepoint();
/*** 第二次轉(zhuǎn)賬 ***/
pstmt = con.prepareStatement(sql_zs2);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls2);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 回滾 (回滾到指定的代碼段)
con.rollback(sp);
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 提交
con.commit();
} catch (SQLException e) {
}
JdbcUtil.closeAll(con, pstmt, null);
}
}
}
Jdbc中大文本類型的處理
- 在實(shí)際開發(fā)中,程序需要把大文本或二進(jìn)制數(shù)據(jù)保存到數(shù)據(jù)庫(kù)
- Oracle中大文本數(shù)據(jù)類型 : Clob(長(zhǎng)文本類型, MySQL中不支持瑞躺,使用的是text), Blob(二進(jìn)制類型)
- MySQL數(shù)據(jù)庫(kù): Text(長(zhǎng)文本類型), Blob(二進(jìn)制類型)
- 基本概念:大數(shù)據(jù)也稱之為L(zhǎng)OB(Large Objects)敷搪,LOB又分為:clob和blob
- clob用于存儲(chǔ)大文本。
- blob用于存儲(chǔ)二進(jìn)制數(shù)據(jù)幢哨,例如圖像赡勘、聲音、二進(jìn)制文等捞镰。
- 對(duì)MySQL而言只有blob闸与,而沒有clob,mysql存儲(chǔ)大文本采用的是Text岸售,Text和blob分別又分為:
- TINYTEXT践樱、TEXT、MEDIUMTEXT和LONGTEXT
- TINYBLOB凸丸、BLOB拷邢、MEDIUMBLOB和LONGBLOB
public class App_text {
// 全局參數(shù)
private Connection con;
private Statement stmt;
private PreparedStatement pstmt;
private ResultSet rs;
@Test
// 1. 保存大文本數(shù)據(jù)類型 ( 寫longtext)
public void testSaveText() {
String sql = "insert into test(content) values(?)";
try {
// 連接
con = JdbcUtil.getConnection();
// pstmt 對(duì)象
pstmt = con.prepareStatement(sql);
// 設(shè)置參數(shù)
// 先獲取文件路徑
String path = App_text.class.getResource("tips.txt").getPath();
FileReader reader = new FileReader(new File(path));
pstmt.setCharacterStream(1, reader);
// 執(zhí)行sql
pstmt.executeUpdate();
// 關(guān)閉
reader.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
@Test
// 2. 讀取大文本數(shù)據(jù)類型 ( 讀longtext)
public void testGetAsText() {
String sql = "select * from test;";
try {
// 連接
con = JdbcUtil.getConnection();
// pstmt 對(duì)象
pstmt = con.prepareStatement(sql);
// 讀取
rs = pstmt.executeQuery();
if (rs.next()) {
// 獲取長(zhǎng)文本數(shù)據(jù), 方式1:
//Reader r = rs.getCharacterStream("content");
// 獲取長(zhǎng)文本數(shù)據(jù), 方式2:
System.out.print(rs.getString("content"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
}
Jdbc綜合練習(xí)
- 需求分析:登陸、注冊(cè)屎慢、注銷瞭稼;
- 登陸成功 : 顯示所有的員工
- 設(shè)計(jì)
- 數(shù)據(jù)庫(kù)設(shè)計(jì):
- Admin, 存放所有的登陸用戶
- Employee, 存放所有的員工信息
- 系統(tǒng)設(shè)計(jì)
- 系統(tǒng)結(jié)構(gòu)
- 分層: 基于mvc模式的分層
- 項(xiàng)目用到的公用組件、類 (了解)
- 編碼
BeanUtils組件簡(jiǎn)介
- 程序中對(duì)javabean的操作很頻繁腻惠, 所以apache提供了一套開源的api环肘,方便對(duì)javabean的操作!即BeanUtils組件(作用是簡(jiǎn)化javabean的操作)
- 用戶可以從www.apache.org下載BeanUtils組件集灌,然后再在項(xiàng)目中引入jar文件悔雹!
導(dǎo)入BenUtils組件
- 引入commons-beanutils-1.8.3.jar核心包
- 引入日志支持包: commons-logging-1.1.3.jar
- 如果缺少日志jar文件,報(bào)錯(cuò):
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.apache.commons.beanutils.ConvertUtilsBean.<init>(ConvertUtilsBean.java:157)
at org.apache.commons.beanutils.BeanUtilsBean.<init>(BeanUtilsBean.java:117)
at org.apache.commons.beanutils.BeanUtilsBean$1.initialValue(BeanUtilsBean.java:68)
實(shí)例, 基本用法
- 方法1: 對(duì)象屬性的拷貝
BeanUtils.copyProperty(admin, "userName", "jack");
BeanUtils.setProperty(admin, "age", 18);
- 方法2: 對(duì)象的拷貝
BeanUtils.copyProperties(newAdmin, admin);
- 方法3: map數(shù)據(jù)拷貝到j(luò)avabean中 (注意:map中的key要與javabean的屬性名稱一致)
BeanUtils.populate(adminMap, map);
public void test1() throws Exception {
// a. 基本操作
Admin admin = **new** Admin();
// admin.setUserName("Jack");
// admin.setPwd("999");
// b. BeanUtils組件實(shí)現(xiàn)對(duì)象屬性的拷貝
BeanUtils.copyProperty(admin, "userName", "jack");
BeanUtils.setProperty(admin, "age", 18); // copy, set 都可以
// 總結(jié)1: 對(duì)于基本數(shù)據(jù)類型绝页,會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換!
// c. 對(duì)象的拷貝
Admin newAdmin = new Admin();
BeanUtils.copyProperties(newAdmin, admin);
// d. map數(shù)據(jù)荠商,拷貝到對(duì)象中
Admin adminMap = new Admin();
Map<String,Object> map = new HashMap<String,Object>();
map.put("userName", "Jerry");
map.put("age", 29);
// 注意:map中的key要與javabean的屬性名稱一致
BeanUtils.populate(adminMap, map);
// 測(cè)試
System.out.println(adminMap.getUserName());
System.out.println(adminMap.getAge());
}
實(shí)例, 日期類型的拷貝
- 需要注冊(cè)日期類型轉(zhuǎn)換器,2種方式參見下面代碼
//2. 自定義日期類型轉(zhuǎn)換器
@Test
public void test2() throws Exception {
// 模擬表單數(shù)據(jù)
String name = "jack";
String age = "20";
String birth = " ";
// 對(duì)象
Admin admin = new Admin();
// 注冊(cè)日期類型轉(zhuǎn)換器:1续誉, 自定義的方式
ConvertUtils.register(new Converter() {
// 轉(zhuǎn)換的內(nèi)部實(shí)現(xiàn)方法莱没,需要重寫
@Override
public Object convert(Class type, Object value) {
// 判斷
if (type != Date.class) {
return null;
}
if (value == null || "".equals(value.toString().trim())) {
return null;
}
try {
// 字符串轉(zhuǎn)換為日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(value.toString());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
},Date.class);
// 把表單提交的數(shù)據(jù),封裝到對(duì)象中
BeanUtils.copyProperty(admin, "userName", name);
BeanUtils.copyProperty(admin, "age", age);
BeanUtils.copyProperty(admin, "birth", birth);
//------ 測(cè)試------
System.out.println(admin);
}
//2. 使用提供的日期類型轉(zhuǎn)換器工具類
@Test
public void test3() throws Exception {
// 模擬表單數(shù)據(jù)
String name = "userName";
String age = "20";
String birth = null;
// 對(duì)象
Admin admin = new Admin();
// 注冊(cè)日期類型轉(zhuǎn)換器:2: 使用組件提供的轉(zhuǎn)換器工具類
ConvertUtils.register(new DateLocaleConverter(), Date.class);
// 把表單提交的數(shù)據(jù)酷鸦,封裝到對(duì)象中
BeanUtils.copyProperty(admin, "userName", name);
BeanUtils.copyProperty(admin, "age", age);
BeanUtils.copyProperty(admin, "birth", birth);
//------ 測(cè)試------
System.out.println(admin);
}
}
應(yīng)用
public class WebUtils {
@Deprecated
public static <T> T copyToBean_old(HttpServletRequest request, Class<T> clazz) {
try {
// 創(chuàng)建對(duì)象
T t = clazz.newInstance();
// 獲取所有的表單元素的名稱
Enumeration<String> enums = request.getParameterNames();
// 遍歷
while (enums.hasMoreElements()) {
// 獲取表單元素的名稱:<input type="password" name="pwd"/>
String name = enums.nextElement(); // pwd
// 獲取名稱對(duì)應(yīng)的值
String value = request.getParameter(name);
// 把指定屬性名稱對(duì)應(yīng)的值進(jìn)行拷貝
BeanUtils.copyProperty(t, name, value);
}
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 處理請(qǐng)求數(shù)據(jù)的封裝
*/
public static <T> T copyToBean(HttpServletRequest request, Class<T> clazz) {
try {
// (注冊(cè)日期類型轉(zhuǎn)換器)
// 創(chuàng)建對(duì)象
T t = clazz.newInstance();
BeanUtils.populate(t, request.getParameterMap()); // 使用map
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
元數(shù)據(jù)
- 在jdbc中獲取數(shù)據(jù)庫(kù)的定義饰躲,例如:數(shù)據(jù)庫(kù)牙咏、表、列的定義信息, 就用到元數(shù)據(jù)
- 在jdbc中可以使用: 數(shù)據(jù)庫(kù)元數(shù)據(jù)嘹裂、參數(shù)元數(shù)據(jù)妄壶、結(jié)果集元數(shù)據(jù)(元數(shù)據(jù)定義相關(guān)api, ..MetaData)
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import org.junit.Test;
import anderson.sh.utils.JdbcUtil; // 自己的工具庫(kù)
public class App {
//1. 數(shù)據(jù)庫(kù)元數(shù)據(jù)
@Test
public void testDB() throws Exception {
// 獲取連接
Connection conn = JdbcUtil.getConnection();
// 獲取數(shù)據(jù)庫(kù)元數(shù)據(jù)
DatabaseMetaData metaData = conn.getMetaData(); // alt + shift + L 快速獲取方法返回值
System.out.println(metaData.getUserName());
System.out.println(metaData.getURL());
System.out.println(metaData.getDatabaseProductName());
}
//2. 參數(shù)元數(shù)據(jù)
@Test
public void testParams() throws Exception {
// 獲取連接
Connection conn = JdbcUtil.getConnection();
// SQL
String sql = "select * from dept where deptid=? and deptName=?";
// Object[] values = {"tom","888"};
PreparedStatement pstmt = conn.prepareStatement(sql);
// 參數(shù)元數(shù)據(jù)
ParameterMetaData p_metaDate = pstmt.getParameterMetaData();
// 獲取參數(shù)的個(gè)數(shù)
int count = p_metaDate.getParameterCount();
// 測(cè)試
System.out.println(count);
}
// 3. 結(jié)果集元數(shù)據(jù)
@Test
public void testRs() throws Exception {
String sql = "select * from dept ";
// 獲取連接
Connection conn = JdbcUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
// 得到結(jié)果集元數(shù)據(jù)(目標(biāo):通過結(jié)果集元數(shù)據(jù)寄狼,得到列的名稱)
ResultSetMetaData rs_metaData = rs.getMetaData();
// 迭代每一行結(jié)果
while (rs.next()) {
// 1. 獲取列的個(gè)數(shù)
int count = rs_metaData.getColumnCount();
// 2. 遍歷丁寄,獲取每一列的列的名稱
for (int i=0; i<count; i++) {
// 得到列的名稱
String columnName = rs_metaData.getColumnName(i + 1);
// 獲取每一行的每一列的值
Object columnValue = rs.getObject(columnName);
// 測(cè)試
System.out.print(columnName + "=" + columnValue + ",");
}
System.out.println();
}
}
}
Dao操作的抽取(BaseDAO)
- Dao操作通用的步驟:
- 寫SQL語(yǔ)句
- 獲取連接
- 創(chuàng)建stmt
- 執(zhí)行sql
- 關(guān)閉/異常
- 通用的DAO(BaseDao)
- 更新 :
public void update(String sql, Object[] paramValues);
- 查詢 :
- 傳入的什么類型的對(duì)象,就封裝為什么類型
- 要求: 列的名稱泊愧,要與指定類型的對(duì)象的屬性名稱一樣)
Public List<T> query (String sql , Object[] paramValues , Class<T> clazz);
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import anderson.sh.utils.JdbcUtil;
/**
* 通用的dao伊磺,自己寫的所有的dao都繼承此類;
* 此類定義了2個(gè)通用的方法:
* 1. 更新
* 2. 查詢
*/
public class BaseDao {
// 初始化參數(shù)
private Connection con;
private PreparedStatement pstmt;
private ResultSet rs;
/**
* 更新的通用方法
* @param sql 更新的sql語(yǔ)句(update/insert/delete)
* @param paramsValue sql語(yǔ)句中占位符對(duì)應(yīng)的值(如果沒有占位符,傳入null)
*/
public void update(String sql,Object[] paramsValue){
try {
// 獲取連接
con = JdbcUtil.getConnection();
// 創(chuàng)建執(zhí)行命令的stmt對(duì)象
pstmt = con.prepareStatement(sql);
// 參數(shù)元數(shù)據(jù): 得到占位符參數(shù)的個(gè)數(shù)
int count = pstmt.getParameterMetaData().getParameterCount();
// 設(shè)置占位符參數(shù)的值
if (paramsValue != null && paramsValue.length > 0) {
// 循環(huán)給參數(shù)賦值
for(int i=0;i<count;i++) {
pstmt.setObject(i+1, paramsValue[i]);
}
}
// 執(zhí)行更新
pstmt.executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
/**
* 查詢的通用方法
* @param sql
* @param paramsValue
*/
public <T> List<T> query(String sql, Object[] paramsValue,Class<T> clazz){
try {
// 返回的集合
List<T> list = new ArrayList<T>();
// 對(duì)象
T t = null;
// 1. 獲取連接
con = JdbcUtil.getConnection();
// 2. 創(chuàng)建stmt對(duì)象
pstmt = con.prepareStatement(sql);
// 3. 獲取占位符參數(shù)的個(gè)數(shù)删咱, 并設(shè)置每個(gè)參數(shù)的值
//int count = pstmt.getParameterMetaData().getParameterCount();
if (paramsValue != null && paramsValue.length > 0) {
for (int i=0; i<paramsValue.length; i++) {
pstmt.setObject(i+1, paramsValue[i]);
}
}
// 4. 執(zhí)行查詢
rs = pstmt.executeQuery();
// 5. 獲取結(jié)果集元數(shù)據(jù)
ResultSetMetaData rsmd = rs.getMetaData();
// ---> 獲取列的個(gè)數(shù)
int columnCount = rsmd.getColumnCount();
// 6. 遍歷rs
while (rs.next()) {
// 要封裝的對(duì)象
t = clazz.newInstance();
// 7. 遍歷每一行的每一列, 封裝數(shù)據(jù)
for (int i=0; i<columnCount; i++) {
// 獲取每一列的列名稱
String columnName = rsmd.getColumnName(i + 1);
// 獲取每一列的列名稱, 對(duì)應(yīng)的值
Object value = rs.getObject(columnName);
// 封裝: 設(shè)置到t對(duì)象的屬性中 【BeanUtils組件】
BeanUtils.copyProperty(t, columnName, value);
}
// 把封裝完畢的對(duì)象屑埋,添加到list集合中
list.add(t);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtil.closeAll(con, pstmt, rs);
}
}
}
import java.util.List;
public class AdminDao extends BaseDao {
// 刪除
public void delete(int id) {
String sql = "delete from admin where id=?";
Object[] paramsValue = {id};
super.update(sql, paramsValue);
}
// 插入
public void save(Admin admin) {
String sql = "insert into admin (userName,pwd) values (?,?)";
Object[] paramsValue = {admin.getUserName(),admin.getPwd()};
super.update(sql, paramsValue);
}
// 查詢?nèi)? public List<Admin> getAll(){
String sql = "select * from admin";
List<Admin> list = super.query(sql, null, Admin.class);
return list;
}
// 根據(jù)條件查詢(主鍵)
public Admin findById(int id){
String sql = "select * from admin where id=?";
List<Admin> list = super.query(sql, new Object[]{id}, Admin.class);
return (list!=null&&list.size()>0) ? list.get(0) : null;
}
}