预编译sql处理(防止sql注入)
- Statement : 执行SQL命令
- CallableStatement : 执行存储过程
- PreparedStatement : 预编译SQL语句执行
- 使用预编译SQL语句的命令对象的好处 : 避免频繁sql拼接 (可以使用占位符), 可以防止sql注入
- 例如: 登陆模块输入用户名,密码的时候, 可以避免用户输入的恶意密码
public class App {
// 连接参数
//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语句
String sql = "select * from admin where userName='"+userName+"' and pwd='"+pwd+"' ";
System.out.println(sql);
try {
// 1.1 加载驱动,创建连接
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);
// 1.2 创建stmt对象
stmt = con.createStatement();
// 1.3 执行查询
rs = stmt.executeQuery(sql);
// 业务判断
if (rs.next()) {
System.out.println("登陆成功, 编号:" + rs.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 1.4 关闭
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语句
String sql = "select * from admin where userName=? and pwd=? ";
try {
// 1.1 加载驱动,创建连接
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);
// 1.2 创建pstmt对象
pstmt = con.prepareStatement(sql); // 对sql语句预编译
// 设置占位符值
pstmt.setString(1, userName);
pstmt.setString(2, pwd);
// 1.3 执行
rs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("登陆成功," + rs.getInt("id"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 1.4 关闭
try {
rs.close();
pstmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
存储过程调用
-- 存储过程
-- 定义分隔符
DELIMITER $$
CREATE PROCEDURE proc_login()
BEGIN
SELECT * FROM admin;
END $$
-- 调用
CALL proc_login;
public class App_call {
// 全局参数
private Connection con;
private Statement stmt;
private PreparedStatement pstmt;
private CallableStatement cstmt; // 存储过程
private ResultSet rs;
// 程序中调用存储过程
@Test
public void testCall() throws Exception {
try {
//1 . 创建连接
con = JdbcUtil.getConnection();
//2. 创建执行存储过程的stmt对象
CallableStatement cstmt = con.prepareCall("CALL proc_login");
//3. 执行(存储过程)
rs = cstmt.executeQuery();
// 遍历结果,测试
if (rs.next()) {
String name = rs.getString("userName");
String pwd = rs.getString("pwd");
// 测试
System.out.println(name + pwd);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
批处理
- 业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
- 执行批处理SQL语句:
- executeBatch()方法:执行批处理命令
- clearBatch()方法:清除批处理命令
- 实现批处理有两种方式,
- 第一种方式:Statement.addBatch(sql) list
- 优点:可以向数据库发送多条不同的SQL语句。
- 缺点:SQL语句没有预编译; 当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。例如:
Insert into user(name,password) values(‘aa’,’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语句加入到批命令中
st.addBatch(sql2); //把SQL语句加入到批命令中
st.executeBatch();
} finally {
JdbcUtil.free(conn, st, rs);
}
- 第二种方式:采用Statement.addBatch(sql)方式实现批处理:
- 优点:发送的是预编译后的SQL语句,执行效率高
- 缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据
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 {
// 测试批处理操作
@Test
public void testBatch() throws Exception {
// 模拟数据
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);
}
}
// 封装所有的与数据库的操作
public class AdminDao {
// 全局参数
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();
// 创建stmt
pstmt = con.prepareStatement(sql); // 【预编译SQL语句】
for (int i=0; i<list.size(); i++) {
Admin admin = list.get(i);
// 设置参数
pstmt.setString(1, admin.getUserName());
pstmt.setString(2, admin.getPwd());
// 添加批处理
pstmt.addBatch(); // 【不需要传入SQL】
// 测试:每5条执行一次批处理
if (i % 5 == 0) {
// 批量执行
pstmt.executeBatch();
// 清空批处理
pstmt.clearBatch();
}
}
// 批量执行
pstmt.executeBatch();
// 清空批处理
pstmt.clearBatch();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, rs);
}
}
}
插入数据,获取自增长值
- 需求 :
李俊杰 : 18, 张相 : 19
- 如何设计数据库?
编号 员工姓名 年龄 部门
01 李俊杰 18 开发部
02 张三 19 开发部
- 思考 : 如何减少数据冗余 -> 设置外键约束(部门与员工, 一对多的关系)
编号 员工姓名 年龄 部门
01 李俊杰 18 1
02 张三 19 1
部门编号 部门名称
1 开发部
员工表 (外键表) 【员工表有一个外键字段,引用了部门表的主键】
部门表(主键表)
- 编码总体思路 : 保存员工及其对应的部门
- 步骤:先保存部门; 再得到部门主键,再保存员工
- 开发具体步骤:
- 设计javabean
- 设计dao
- 测试
部门
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;
// 保存员工,同时保存关联的部门
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();
/*****保存部门,获取自增长*******/
// 【一、需要指定返回自增长标记】
pstmt = con.prepareStatement(sql_dept,Statement.RETURN_GENERATED_KEYS);
// 设置参数
pstmt.setString(1, emp.getDept().getDeptName());
// 执行
pstmt.executeUpdate();
// 【二、获取上面保存的部门子增长的主键】
rs = pstmt.getGeneratedKeys();
// 得到返回的自增长字段
if (rs.next()) {
deptId = rs.getInt(1);
}
/*****保存员工*********/
pstmt = con.prepareStatement(sql_emp);
// 设置参数
pstmt.setString(1, emp.getEmpName());
pstmt.setInt(2, deptId);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, rs);
}
}
}
事务
- 基本概念 : 事务使指一组最小逻辑操作单元,里面有多个操作组成。 组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚
- 事务ACID特性 :
- 原子性(Atomicity) : 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
- 一致性(Consistency) : 事务必须使数据库从一个一致性状态变换到另外一个一致性状态
- 隔离性(Isolation) : 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
- 持久性(Durability) : 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
- 事务的特性:
- 原子性,是一个最小逻辑操作单元
- 一致性,事务过程中,数据处于一致状态
- 持久性, 事务一旦提交成功,对数据的更改会反映到数据库中
- 隔离性, 事务与事务之间是隔离的
- 案例 :
- 需求 : 张三给李四转账
- 设计 : 账户表
- 技术 :
- Connection :
-
void setAutoCommit(boolean autoCommit)
: 设置事务是否自动提交; 如果设置为false,表示手动提交事务
-
void commit()
: 手动提交事务
-
void rollback()
: 回滚 (出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态)
Savepoint setSavepoint(String name)
- 代码示例 :
-- 账户表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
accountName VARCHAR(20),
money DOUBLE
);
-- 转账
UPDATE account SET money=money-1000 WHERE accountName='张三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
public class AccountDao {
// 全局参数
private Connection con;
private PreparedStatement pstmt;
// 1. 转账,没有使用事务
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(); // 默认开启的隐士事务
con.setAutoCommit(true);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
// 2. 转账,使用事务
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(); // 默认开启的隐士事务
// 一、设置事务为手动提交
con.setAutoCommit(false);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 二、 出现异常,需要回滚事务
con.rollback();
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 三、所有的操作执行成功, 提交事务
con.commit();
JdbcUtil.closeAll(con, pstmt, null);
} catch (SQLException e) {
}
}
}
// 3. 转账,使用事务, 回滚到指定的代码段
public void trans() {
// 定义个标记
Savepoint sp = null;
// 第一次转账
String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
// 第二次转账
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(); // 默认开启的隐士事务
con.setAutoCommit(false); // 设置事务手动提交
/*** 第一次转账 ***/
pstmt = con.prepareStatement(sql_zs1);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls1);
pstmt.executeUpdate();
// 回滚到这个位置?
sp = con.setSavepoint();
/*** 第二次转账 ***/
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中大文本类型的处理
- 在实际开发中,程序需要把大文本或二进制数据保存到数据库
- Oracle中大文本数据类型 : Clob(长文本类型, MySQL中不支持,使用的是text), Blob(二进制类型)
- MySQL数据库: Text(长文本类型), Blob(二进制类型)
- 基本概念:大数据也称之为LOB(Large Objects),LOB又分为:clob和blob
- clob用于存储大文本。
- blob用于存储二进制数据,例如图像、声音、二进制文等。
- 对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
- TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
- TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
public class App_text {
// 全局参数
private Connection con;
private Statement stmt;
private PreparedStatement pstmt;
private ResultSet rs;
@Test
// 1. 保存大文本数据类型 ( 写longtext)
public void testSaveText() {
String sql = "insert into test(content) values(?)";
try {
// 连接
con = JdbcUtil.getConnection();
// pstmt 对象
pstmt = con.prepareStatement(sql);
// 设置参数
// 先获取文件路径
String path = App_text.class.getResource("tips.txt").getPath();
FileReader reader = new FileReader(new File(path));
pstmt.setCharacterStream(1, reader);
// 执行sql
pstmt.executeUpdate();
// 关闭
reader.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
@Test
// 2. 读取大文本数据类型 ( 读longtext)
public void testGetAsText() {
String sql = "select * from test;";
try {
// 连接
con = JdbcUtil.getConnection();
// pstmt 对象
pstmt = con.prepareStatement(sql);
// 读取
rs = pstmt.executeQuery();
if (rs.next()) {
// 获取长文本数据, 方式1:
//Reader r = rs.getCharacterStream("content");
// 获取长文本数据, 方式2:
System.out.print(rs.getString("content"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeAll(con, pstmt, null);
}
}
}
Jdbc综合练习
- 需求分析:登陆、注册、注销;
- 登陆成功 : 显示所有的员工
- 设计
- 数据库设计:
- Admin, 存放所有的登陆用户
- Employee, 存放所有的员工信息
- 系统设计
- 系统结构
- 分层: 基于mvc模式的分层
- 项目用到的公用组件、类 (了解)
- 编码
BeanUtils组件简介
- 程序中对javabean的操作很频繁, 所以apache提供了一套开源的api,方便对javabean的操作!即BeanUtils组件(作用是简化javabean的操作)
- 用户可以从www.apache.org下载BeanUtils组件,然后再在项目中引入jar文件!
导入BenUtils组件
- 引入commons-beanutils-1.8.3.jar核心包
- 引入日志支持包: commons-logging-1.1.3.jar
- 如果缺少日志jar文件,报错:
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)
实例, 基本用法
- 方法1: 对象属性的拷贝
BeanUtils.copyProperty(admin, "userName", "jack");
BeanUtils.setProperty(admin, "age", 18);
- 方法2: 对象的拷贝
BeanUtils.copyProperties(newAdmin, admin);
- 方法3: map数据拷贝到javabean中 (注意: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组件实现对象属性的拷贝
BeanUtils.copyProperty(admin, "userName", "jack");
BeanUtils.setProperty(admin, "age", 18); // copy, set 都可以
// 总结1: 对于基本数据类型,会自动进行类型转换!
// c. 对象的拷贝
Admin newAdmin = new Admin();
BeanUtils.copyProperties(newAdmin, admin);
// d. map数据,拷贝到对象中
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);
// 测试
System.out.println(adminMap.getUserName());
System.out.println(adminMap.getAge());
}
实例, 日期类型的拷贝
//2. 自定义日期类型转换器
@Test
public void test2() throws Exception {
// 模拟表单数据
String name = "jack";
String age = "20";
String birth = " ";
// 对象
Admin admin = new Admin();
// 注册日期类型转换器:1, 自定义的方式
ConvertUtils.register(new Converter() {
// 转换的内部实现方法,需要重写
@Override
public Object convert(Class type, Object value) {
// 判断
if (type != Date.class) {
return null;
}
if (value == null || "".equals(value.toString().trim())) {
return null;
}
try {
// 字符串转换为日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(value.toString());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
},Date.class);
// 把表单提交的数据,封装到对象中
BeanUtils.copyProperty(admin, "userName", name);
BeanUtils.copyProperty(admin, "age", age);
BeanUtils.copyProperty(admin, "birth", birth);
//------ 测试------
System.out.println(admin);
}
//2. 使用提供的日期类型转换器工具类
@Test
public void test3() throws Exception {
// 模拟表单数据
String name = "userName";
String age = "20";
String birth = null;
// 对象
Admin admin = new Admin();
// 注册日期类型转换器:2: 使用组件提供的转换器工具类
ConvertUtils.register(new DateLocaleConverter(), Date.class);
// 把表单提交的数据,封装到对象中
BeanUtils.copyProperty(admin, "userName", name);
BeanUtils.copyProperty(admin, "age", age);
BeanUtils.copyProperty(admin, "birth", birth);
//------ 测试------
System.out.println(admin);
}
}
应用
public class WebUtils {
@Deprecated
public static <T> T copyToBean_old(HttpServletRequest request, Class<T> clazz) {
try {
// 创建对象
T t = clazz.newInstance();
// 获取所有的表单元素的名称
Enumeration<String> enums = request.getParameterNames();
// 遍历
while (enums.hasMoreElements()) {
// 获取表单元素的名称:<input type="password" name="pwd"/>
String name = enums.nextElement(); // pwd
// 获取名称对应的值
String value = request.getParameter(name);
// 把指定属性名称对应的值进行拷贝
BeanUtils.copyProperty(t, name, value);
}
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 处理请求数据的封装
*/
public static <T> T copyToBean(HttpServletRequest request, Class<T> clazz) {
try {
// (注册日期类型转换器)
// 创建对象
T t = clazz.newInstance();
BeanUtils.populate(t, request.getParameterMap()); // 使用map
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
元数据
- 在jdbc中获取数据库的定义,例如:数据库、表、列的定义信息, 就用到元数据
- 在jdbc中可以使用: 数据库元数据、参数元数据、结果集元数据(元数据定义相关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; // 自己的工具库
public class App {
//1. 数据库元数据
@Test
public void testDB() throws Exception {
// 获取连接
Connection conn = JdbcUtil.getConnection();
// 获取数据库元数据
DatabaseMetaData metaData = conn.getMetaData(); // alt + shift + L 快速获取方法返回值
System.out.println(metaData.getUserName());
System.out.println(metaData.getURL());
System.out.println(metaData.getDatabaseProductName());
}
//2. 参数元数据
@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);
// 参数元数据
ParameterMetaData p_metaDate = pstmt.getParameterMetaData();
// 获取参数的个数
int count = p_metaDate.getParameterCount();
// 测试
System.out.println(count);
}
// 3. 结果集元数据
@Test
public void testRs() throws Exception {
String sql = "select * from dept ";
// 获取连接
Connection conn = JdbcUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
// 得到结果集元数据(目标:通过结果集元数据,得到列的名称)
ResultSetMetaData rs_metaData = rs.getMetaData();
// 迭代每一行结果
while (rs.next()) {
// 1. 获取列的个数
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);
// 测试
System.out.print(columnName + "=" + columnValue + ",");
}
System.out.println();
}
}
}
Dao操作的抽取(BaseDAO)
- Dao操作通用的步骤:
- 写SQL语句
- 获取连接
- 创建stmt
- 执行sql
- 关闭/异常
- 通用的DAO(BaseDao)
- 更新 :
public void update(String sql, Object[] paramValues);
- 查询 :
- 传入的什么类型的对象,就封装为什么类型
- 要求: 列的名称,要与指定类型的对象的属性名称一样)
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个通用的方法:
* 1. 更新
* 2. 查询
*/
public class BaseDao {
// 初始化参数
private Connection con;
private PreparedStatement pstmt;
private ResultSet rs;
/**
* 更新的通用方法
* @param sql 更新的sql语句(update/insert/delete)
* @param paramsValue sql语句中占位符对应的值(如果没有占位符,传入null)
*/
public void update(String sql,Object[] paramsValue){
try {
// 获取连接
con = JdbcUtil.getConnection();
// 创建执行命令的stmt对象
pstmt = con.prepareStatement(sql);
// 参数元数据: 得到占位符参数的个数
int count = pstmt.getParameterMetaData().getParameterCount();
// 设置占位符参数的值
if (paramsValue != null && paramsValue.length > 0) {
// 循环给参数赋值
for(int i=0;i<count;i++) {
pstmt.setObject(i+1, paramsValue[i]);
}
}
// 执行更新
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>();
// 对象
T t = null;
// 1. 获取连接
con = JdbcUtil.getConnection();
// 2. 创建stmt对象
pstmt = con.prepareStatement(sql);
// 3. 获取占位符参数的个数, 并设置每个参数的值
//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. 执行查询
rs = pstmt.executeQuery();
// 5. 获取结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
// ---> 获取列的个数
int columnCount = rsmd.getColumnCount();
// 6. 遍历rs
while (rs.next()) {
// 要封装的对象
t = clazz.newInstance();
// 7. 遍历每一行的每一列, 封装数据
for (int i=0; i<columnCount; i++) {
// 获取每一列的列名称
String columnName = rsmd.getColumnName(i + 1);
// 获取每一列的列名称, 对应的值
Object value = rs.getObject(columnName);
// 封装: 设置到t对象的属性中 【BeanUtils组件】
BeanUtils.copyProperty(t, columnName, value);
}
// 把封装完毕的对象,添加到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);
}
// 查询全部
public List<Admin> getAll(){
String sql = "select * from admin";
List<Admin> list = super.query(sql, null, Admin.class);
return list;
}
// 根据条件查询(主键)
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;
}
}