第18天(就业班) 预编译sql处理(防止sql注入)、存储过程、批处理、插入数据获取增长值、事务、大文本类型处理
2017-02-06 12:43
701 查看
一、预编译sql处理(防止sql注入)
--创建数据库
CREATEDATABASE jdbc_demo DEFAULT CHARACTER SET utf8;i
--创建表
USEjdbc_demo;
CREATETABLE admin(
id INT PRIMARY KEY AUTO_INCREMENT,
userName VARCHAR(20),
pwd VARCHAR(20)
)
|--Statement 执行SQL命令
|-- CallableStatement, 执行存储过程
|--PreparedStatement 预编译SQL语句执行
使用预编译SQL语句的命令对象,好处:
1. 避免了频繁sql拼接 (可以使用占位符)
2. 可以防止sql注入
登陆模块,
输入用户名,密码!
注意,
要避免用户输入的恶意密码!
package com.xp.jdbc.pstmt;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
public class PstmtLogin {
// 连接参数
// 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;
package com.xp.jdbc.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtil {
// 连接参数
// private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
private static String url = "jdbc:mysql:///jdbc_demo";
private static String user = "root";
private static String password = "xiongpan";
/**
* 返回连接对象
*/
public static Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection(url, user, password);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 关闭
*/
public static void closeAll(Connection con, Statement stmt, ResultSet rs) {
try {
if (rs != null) {
rs.close(); // 快速异常捕获 Alt + shift + z
rs = null; // 建议垃圾回收期回收资源
}
if (stmt != null) {
stmt.close();
stmt = null;
}
if (con != null && !con.isClosed()) {
con.close();
con = null;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
package com.xp.jdbc.pstmt;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
import com.xp.jdbc.utils.JdbcUtil;
public class PstmtLogin_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语句!
需求:批量保存信息!
设计:
AdminDao
Public void save(List<Admin list){ // 目前用这种方式
// 循环
// 保存 (批量保存)
}
Public void save(Admin admin ){
// 循环
// 保存
}
技术:
|-- Statement
批处理相关方法
voidaddBatch(String sql) 添加批处理
voidclearBatch() 清空批处理
int[] executeBatch() 执行批处理
实现:
Admin.java 实体类封装数据
AdminDao.java 封装所有的与数据库的操作
App.java 测试
需求:
李俊杰 18
张相 19
如何设计数据库?
编号 员工姓名 年龄 部门
01 李俊杰 18 开发部
02 张三 19 开发部’
思考:
如何减少数据冗余?
设置外键约束
所以,
编号 员工姓名 年龄 部门
01 李俊杰 18 1
02 张三 19 1
部门编号 部门名称
1 开发部
部门与员工,
一对多的关系
设计数据库:
员工表 (外键表) 【员工表有一个外键字段,引用了部门表的主键】
部门表(主键表)
编码总体思路:
保存员工及其对应的部门!
步骤:
1. 先保存部门
2. 再得到部门主键,再保存员工
开发具体步骤:
1. 设计javabean
2. 设计dao
3. 测试
-- 部门
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) ;
package com.xp.auto;
public class Employee {
private int empId;
private String empName;
// 关联的部门
private Dept dept;
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
package com.xp.auto;
public class Dept {
private int id;
private String deptName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
package com.xp.auto;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.xp.jdbc.utils.JdbcUtil;
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);
}
}
}
package com.xp.auto;
import org.junit.Test;
public class App {
// 保存员工
@Test
public void testSave() throws Exception {
// 模拟数据
Dept d = new Dept();
d.setDeptName("应用开发部");
Employee emp = new Employee();
emp.setEmpName("李俊杰");
emp.setDept(d); // 关联
// 调用dao保存
EmpDao empDao = new EmpDao();
empDao.save(emp);
}
}
五、事务
事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。
事务ACID特性
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
事务的特性:
原子性,是一个最小逻辑操作单元 !
一致性,事务过程中,数据处于一致状态。
持久性, 事务一旦提交成功,对数据的更改会反映到数据库中。
隔离性, 事务与事务之间是隔离的。
案例:
需求: 张三给李四转账
设计: 账户表
技术:
|--Connection
voidsetAutoCommit(boolean autoCommit) ; 设置事务是否自动提交如果设置为false,表示手动提交事务。
voidcommit() (); 手动提交事务
voidrollback() ; 回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)
SavepointsetSavepoint(String name)
Oracle中大文本数据类型,
Clob 长文本类型 (MySQL中不支持,使用的是text)
Blob 二进制类型
MySQL数据库,
Text 长文本类型
Blob 二进制类型
需求: jdbc中操作长文本数据。
设计: 测试表
编码:
保存大文本数据类型
读取大文本数据类型
保存二进制数据
读取二进制数据
-- 测试大数据类型
五、综合案例
需求分析:
登陆、注册、注销;
登陆成功,
显示所有的员工
设计
数据库设计:
Admin, 存放所有的登陆用户
Employee, 存放所有的员工信息
系统设计
a. 系统结构
分层: 基于mvc模式的分层
b. 项目用到的公用组件、类 (了解)
![](https://img-blog.csdn.net/20170206124204002)
--创建数据库
CREATEDATABASE jdbc_demo DEFAULT CHARACTER SET utf8;i
--创建表
USEjdbc_demo;
CREATETABLE admin(
id INT PRIMARY KEY AUTO_INCREMENT,
userName VARCHAR(20),
pwd VARCHAR(20)
)
|--Statement 执行SQL命令
|-- CallableStatement, 执行存储过程
|--PreparedStatement 预编译SQL语句执行
使用预编译SQL语句的命令对象,好处:
1. 避免了频繁sql拼接 (可以使用占位符)
2. 可以防止sql注入
登陆模块,
输入用户名,密码!
注意,
要避免用户输入的恶意密码!
package com.xp.jdbc.pstmt;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
public class PstmtLogin {
// 连接参数
// 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;
package com.xp.jdbc.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtil {
// 连接参数
// private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
private static String url = "jdbc:mysql:///jdbc_demo";
private static String user = "root";
private static String password = "xiongpan";
/**
* 返回连接对象
*/
public static Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection(url, user, password);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 关闭
*/
public static void closeAll(Connection con, Statement stmt, ResultSet rs) {
try {
if (rs != null) {
rs.close(); // 快速异常捕获 Alt + shift + z
rs = null; // 建议垃圾回收期回收资源
}
if (stmt != null) {
stmt.close();
stmt = null;
}
if (con != null && !con.isClosed()) {
con.close();
con = null;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
package com.xp.jdbc.pstmt;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
import com.xp.jdbc.utils.JdbcUtil;
public class PstmtLogin_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语句!
需求:批量保存信息!
设计:
AdminDao
Public void save(List<Admin list){ // 目前用这种方式
// 循环
// 保存 (批量保存)
}
Public void save(Admin admin ){
// 循环
// 保存
}
技术:
|-- Statement
批处理相关方法
voidaddBatch(String sql) 添加批处理
voidclearBatch() 清空批处理
int[] executeBatch() 执行批处理
实现:
Admin.java 实体类封装数据
AdminDao.java 封装所有的与数据库的操作
App.java 测试
package com.xp.batch; public class Admin { private String userName; private String pwd; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } } package com.xp.batch; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import com.xp.jdbc.utils.JdbcUtil; 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); } } } package com.xp.batch; import java.util.ArrayList; import java.util.List; import org.junit.Test; 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); } }四、 插入数据,获取自增长值
需求:
李俊杰 18
张相 19
如何设计数据库?
编号 员工姓名 年龄 部门
01 李俊杰 18 开发部
02 张三 19 开发部’
思考:
如何减少数据冗余?
设置外键约束
所以,
编号 员工姓名 年龄 部门
01 李俊杰 18 1
02 张三 19 1
部门编号 部门名称
1 开发部
部门与员工,
一对多的关系
设计数据库:
员工表 (外键表) 【员工表有一个外键字段,引用了部门表的主键】
部门表(主键表)
编码总体思路:
保存员工及其对应的部门!
步骤:
1. 先保存部门
2. 再得到部门主键,再保存员工
开发具体步骤:
1. 设计javabean
2. 设计dao
3. 测试
-- 部门
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) ;
package com.xp.auto;
public class Employee {
private int empId;
private String empName;
// 关联的部门
private Dept dept;
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
package com.xp.auto;
public class Dept {
private int id;
private String deptName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
package com.xp.auto;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.xp.jdbc.utils.JdbcUtil;
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);
}
}
}
package com.xp.auto;
import org.junit.Test;
public class App {
// 保存员工
@Test
public void testSave() throws Exception {
// 模拟数据
Dept d = new Dept();
d.setDeptName("应用开发部");
Employee emp = new Employee();
emp.setEmpName("李俊杰");
emp.setDept(d); // 关联
// 调用dao保存
EmpDao empDao = new EmpDao();
empDao.save(emp);
}
}
五、事务
事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。
事务ACID特性
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
事务的特性:
原子性,是一个最小逻辑操作单元 !
一致性,事务过程中,数据处于一致状态。
持久性, 事务一旦提交成功,对数据的更改会反映到数据库中。
隔离性, 事务与事务之间是隔离的。
案例:
需求: 张三给李四转账
设计: 账户表
技术:
|--Connection
voidsetAutoCommit(boolean autoCommit) ; 设置事务是否自动提交如果设置为false,表示手动提交事务。
voidcommit() (); 手动提交事务
voidrollback() ; 回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)
SavepointsetSavepoint(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='李四'; package com.xp.account; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Savepoint; import com.xp.jdbc.utils.JdbcUtil; 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); } } } package com.xp.account; import org.junit.Test; public class App { @Test public void testname() throws Exception { // 转账 AccountDao accountDao = new AccountDao(); accountDao.trans(); } }六、Jdbc中大文本类型的处理
Oracle中大文本数据类型,
Clob 长文本类型 (MySQL中不支持,使用的是text)
Blob 二进制类型
MySQL数据库,
Text 长文本类型
Blob 二进制类型
需求: jdbc中操作长文本数据。
设计: 测试表
编码:
保存大文本数据类型
读取大文本数据类型
保存二进制数据
读取二进制数据
-- 测试大数据类型
CREATE TABLE test( id INT PRIMARY KEY AUTO_INCREMENT, content LONGTEXT, img LONGBLOB ); package com.xp.longtext; import java.io.File; import java.io.FileReader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import org.junit.Test; import com.xp.jdbc.utils.JdbcUtil; 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); } } } package com.xp.longtext; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import org.junit.Test; import com.xp.jdbc.utils.JdbcUtil; public class App_blob { // 全局参数 private Connection con; private Statement stmt; private PreparedStatement pstmt; private ResultSet rs; @Test // 1. 二进制数据类型 ( 写longblob) public void testSaveText() { String sql = "insert into test(img) values(?)"; try { // 连接 con = JdbcUtil.getConnection(); // pstmt 对象 pstmt = con.prepareStatement(sql); // 获取图片流 InputStream in = App_text.class.getResourceAsStream("7.jpg"); pstmt.setBinaryStream(1, in); // 执行保存图片 pstmt.execute(); // 关闭 in.close(); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtil.closeAll(con, pstmt, null); } } @Test // 2. 读取大文本数据类型 ( 读longblob) public void testGetAsText() { String sql = "select img from test where id=2;"; try { // 连接 con = JdbcUtil.getConnection(); // pstmt 对象 pstmt = con.prepareStatement(sql); // 读取 rs = pstmt.executeQuery(); if (rs.next()) { // 获取图片流 InputStream in = rs.getBinaryStream("img"); // 图片输出流 FileOutputStream out = new FileOutputStream(new File( "c://1.jpg")); int len = -1; byte b[] = new byte[1024]; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } // 关闭 out.close(); in.close(); } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtil.closeAll(con, pstmt, null); } } }
五、综合案例
需求分析:
登陆、注册、注销;
登陆成功,
显示所有的员工
设计
数据库设计:
Admin, 存放所有的登陆用户
Employee, 存放所有的员工信息
系统设计
a. 系统结构
分层: 基于mvc模式的分层
b. 项目用到的公用组件、类 (了解)
CREATE TABLE admin( id INT PRIMARY KEY AUTO_INCREMENT, userName VARCHAR(20), pwd VARCHAR(20) ) -- 员工 CREATE TABLE employee( empId INT PRIMARY KEY AUTO_INCREMENT, empName VARCHAR(20), dept_id INT -- 外键字段 ); package com.xp.entity; public class Admin { private int id; private String userName; private String pwd; public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } package com.xp.dao; import com.xp.entity.Admin; public interface IAdminDao { /** * 保存 * @param admin */ void save(Admin admin); /** * 根据用户名密码查询 */ Admin findByNameAndPwd(Admin admin); /** * 检查用户名是否存在 * @param name 要检查的用户名 * @return true表示用户名已经存在; 否则用户名不存在 */ boolean userExists(String name); } package com.xp.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.xp.dao.IAdminDao; import com.xp.entity.Admin; import com.xp.utils.JdbcUtil; /** * 2. 数据访问层接口的实现类 */ public class AdminDao implements IAdminDao { private Connection con; private PreparedStatement pstmt; private ResultSet rs; @Override public Admin findByNameAndPwd(Admin admin) { String sql = "select * from admin where userName=? and pwd=?"; Admin ad = null; try { con = JdbcUtil.getConnection(); pstmt = con.prepareStatement(sql); pstmt.setString(1, admin.getUserName()); pstmt.setString(2, admin.getPwd()); // 执行 rs = pstmt.executeQuery(); // 遍历 if (rs.next()) { ad = new Admin(); ad.setId(rs.getInt("id")); ad.setUserName(rs.getString("userName")); ad.setPwd(rs.getString("pwd")); } return ad; } catch (Exception e) { throw new RuntimeException(e); } finally { JdbcUtil.closeAll(con, pstmt, null); } } @Override public void save(Admin admin) { String sql = "INSERT INTO admin(userName,pwd) VALUES(?,?);"; try { con = JdbcUtil.getConnection(); pstmt = con.prepareStatement(sql); // 设置参数 pstmt.setString(1, admin.getUserName()); pstmt.setString(2, admin.getPwd()); // 执行更新 pstmt.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } finally { JdbcUtil.closeAll(con, pstmt, null); } } @Override public boolean userExists(String name) { String sql = "select id from admin where userName=?"; try { con = JdbcUtil.getConnection(); pstmt = con.prepareStatement(sql); // 设置参数 pstmt.setString(1, name); // 执行查询 rs = pstmt.executeQuery(); // 判断 if (rs.next()) { int id = rs.getInt("id"); if (id > 0) { // 用户名已经存在 return true; } } return false; } catch (SQLException e) { throw new RuntimeException(e); } finally { JdbcUtil.closeAll(con, pstmt, rs); } } } package com.xp.service; import com.xp.entity.Admin; import com.xp.exception.UserExistsException; public interface IAdminService { /** * 注册 */ void register(Admin admin) throws UserExistsException; /** * 登陆 */ Admin login(Admin admin); } package com.xp.exception; public class UserExistsException extends Exception { public UserExistsException() { } public UserExistsException(String message) { super(message); } public UserExistsException(Throwable cause) { super(cause); } public UserExistsException(String message, Throwable cause) { super(message, cause); } } package com.xp.service.impl; import com.xp.dao.IAdminDao; import com.xp.dao.impl.AdminDao; import com.xp.entity.Admin; import com.xp.exception.UserExistsException; import com.xp.service.IAdminService; /** * 3. 业务逻辑层实现 * * @author Jie.Yuan * */ public class AdminService implements IAdminService { // 调用的dao private IAdminDao adminDao = new AdminDao(); @Override public Admin login(Admin admin) { try { return adminDao.findByNameAndPwd(admin); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void register(Admin admin) throws UserExistsException { try { // 1. 先根据用户名查询用户是否存在 boolean flag = adminDao.userExists(admin.getUserName()); // 2. 如果用户存在,不允许注册 if (flag) { // 不允许注册, 给调用者提示 throw new UserExistsException("用户名已经存在,注册失败!"); } // 3. 用户不存在,才可以注册 adminDao.save(admin); } catch (UserExistsException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } } package com.xp.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xp.entity.Admin; import com.xp.exception.UserExistsException; import com.xp.service.IAdminService; import com.xp.service.impl.AdminService; public class AdminServlet extends HttpServlet { // 调用的service private IAdminService adminService = new AdminService(); public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取操作类型 String method = request.getParameter("method"); if ("register".equals(method)) { register(request, response); } } /** * 注册处理方法 * * @param request * @param response * @throws IOException * @throws ServletException */ private void register(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 1. 获取请求参数 String userName = request.getParameter("userName"); String pwd = request.getParameter("pwd"); // 封装 Admin admin = new Admin(); admin.setUserName(userName); admin.setPwd(pwd); // 2. 调用Service处理注册的业务逻辑 try { adminService.register(admin); // 注册成功,跳转到首页 request.getRequestDispatcher("/index.jsp").forward(request, response); } catch (UserExistsException e) { // 用户名存在,注册失败(跳转到注册页面) request.setAttribute("message", "用户名已经存在"); // 转发 request.getRequestDispatcher("/register.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); // 测试时候用 // 其他错误, 跳转到错误页面 response.sendRedirect(request.getContextPath() + "/error/error.jsp"); } } } <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>注册</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <form name="frm1" action="${pageContext.request.contextPath }/admin?method=register" method="post" > <table> <tr> <td>用户名</td> <td> <input type="text" name="userName"/> ${requestScope.message } <!-- 如果用户名存在注册失败,给用户提示 --> </td> </tr> <tr> <td>密码</td> <td><input type="password" name="pwd"/></td> </tr> <tr> <td colspan="2"> <input type="submit" value="亲,点我注册!"> </td> </tr> </table> </form> </body> </html>
相关文章推荐
- JDBC(二)--批处理/JDBC获取自增长值/处理大数据文件/事务
- sql 批处理、获取自增长、事务、大文本处理
- 预编译sql处理(防止sql注入)
- SSIS(SQL Server Integration Service)处理导入数据时, 存在的更新, 不存在的插入
- SQL技巧:如何获取存储过程用return返回的值与判断SQL Server事务是否执行成功?
- 一个jdbc的例子(包含sql语句的批处理,事务处理,数据绑定prepare,)
- Visual Studio.Net 2005中用SqlDataSource处理数据库特殊数据类型
- 一个jdbc的例子(包含sql语句的批处理,事务处理,数据绑定prepare)
- Oracle Sql插入数据中特殊字符的处理
- Transact-SQL 示例 - 如何获取一个变量的数据类型
- 事务处理之连续插入中获取操作1中自动生成的key,用于操作2中的外键
- jdbc的Date类型处理,解决java.util.date与java.sql.date的时间数据丢失
- [哭]使用Data Access 2 插入Ntext类型数据时SqlParameter不指定类型的问题 , ,请为我解惑
- 应用VS2005 SQL事务和批量插入数据
- SQL获取插入后数据ID的三种方式比较
- 事务的两种用法(1.在SQL中写存储过程 2.在ADO.NET中处理)各有优点
- SQL:事务检查xx与应用表的重复性,同时向两个表中同时插入数据...
- 用SQL语句获取MSSQL表关键字段的名称、数据类型和长度
- 对于C#中插入数据(Ntext)类型超过大小8000提示处理!
- 获取某个表的数据,并产生插入这些数据的SQL(主要用于不同服务器间相同结构表的数据复制)。