JDBC的全称是Java DataBase Connection,也就是Java数据库连接,我们可以用它来操作关系型数据库。
JDBC接口及相关类在 java.sql 包和 javax.sql 包里。用它来连接数据库,执行操作关系型数据库。
JDBC接口让Java程序和JDBC驱动实现了松耦合(通过JDBC接口完成的,而驱动只有在通过Class.forName反射机制来加载的时候才会出现),使得切换不同的数据库变得更加简单。
二、JDBC的四大物件及其作用
1)DriverManager 类,负责管理一组数据库驱动 2)Connection, 由 DriverManager 产生,负责连接指定的数据库 3)Statement, 由 Connection 对象产生,负责发送sql指令到数据库执行 4)ResultSet,负责接收查询的结果集
PreparedStatement : 可防止sql 注入 1)Statement 的一个子接口 2)适用于循环中使用,或参数有变化的情况 3)重复的指令只需要编译一次,而普通的 Statement 每次执行都需要编译 4)给未知?赋值:setXxx( 1,value),setXxx( 2,value) ..
CallableStatement: 调用存过或函数时使用,也是 Statement 的一个子接口。
三、JDBC操作数据库的步骤
1)导入 mysql 架包,bulid path. java_demo/lib/mysql-connector-java-5.1.46.jar
2)封装 DbUtil 类
加载数据库驱动。
mysql: Class.forName("com.mysql.jdbc.Driver") ;
oracle: Class.forName("oracle.jdbc.driver.OracleDriver") ;
连接数据库。
中文处理:
jdbc:mysql://localhost:3306/test_demo?useUnicode=true&characterEncoding=UTF-8
mysql: return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test_demo","root","123456") ;
oracle: return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","scott","tiger") ;
关闭数据库连接
3)model 包 一个 javabean
4) DAO 包
获取数据库连接
创建一个Statement。
执行SQL语句。
处理结果集。
关闭数据库连接。
5) service 操作
四、小Demo
model
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//省略getter setter
}
DbUtil
import java.sql.*;
//数据库工具类:封装数据库驱动、连接、关闭的方法
public class DbUtil {
public static final String URL = "jdbc:mysql://localhost:3306/test_demo";
public static final String USER = "root";
public static final String PASSWORD = "123456";
//1:加载数据库驱动
static {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("mysql驱动加载成功!");
} catch (ClassNotFoundException e) {
System.err.println("mysql驱动加载失败!");
e.printStackTrace();
}
}
//2. 连接数据库封装成工具类的静态方法
public static Connection getConn () throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
//3.关闭数据库的方法
public static void close(ResultSet rs,Statement stat,Connection conn){
try {
if(rs!=null)
rs.close();
if(stat!=null)
stat.close();
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
DAO
public class StudentDao {
//增加
public void addStu(Student stu) {
Connection conn = null;
Statement stmt = null;
String sql = null;
try {
//不安全,防止sql注入
sql = "insert into student(name,sex,age) values('" + stu.getName() +"','"+ stu.getSex() +"',"+ stu.getAge() +")";
//1. 获取数据库连接
conn = DbUtil.getConn();
//2. 创建Statment对象
stmt = conn.createStatement();
//3. 发送sql指令到数据库服务器执行
stmt.execute(sql);
System.out.println("添加成功");
} catch (SQLException e) {
System.err.println("添加失败:"+sql);
e.printStackTrace();
}finally {
DbUtil.close(null, stmt, conn);
}
}
//修改
public void editStu(Student stu) {
Connection conn = null;
Statement stmt = null;
String sql = null;
try {
//不安全,防止sql注入
sql = "update student set name='" + stu.getName() +"',age=" + stu.getAge() + " where id=1";
conn = DbUtil.getConn();
stmt = conn.createStatement();
stmt.executeUpdate(sql);
System.out.println("修改成功");
} catch (SQLException e) {
System.err.println("修改失败:"+sql);
e.printStackTrace();
} finally {
DbUtil.close(null, stmt, conn);
}
}
//查询所有
public ArrayList getList(){
ArrayList list = new ArrayList();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String sql = null;
try {
sql = "select * from student";
conn = DbUtil.getConn();
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while(rs.next()) {
//创建Student对象存放记录的值
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
list.add(stu);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtil.close(null, stmt, conn);
}
return list;
}
//删除
public void deleteStu(String name) {
Connection conn = null;
Statement stmt = null;
String sql = null;
try {
//不安全,防止sql注入
sql = "delete from student where name='" + name +"'";
conn = DbUtil.getConn();
stmt = conn.createStatement();
stmt.executeUpdate(sql);
System.out.println("删除成功");
} catch (SQLException e) {
System.err.println("删除失败:"+sql);
e.printStackTrace();
} finally {
DbUtil.close(null, stmt, conn);
}
}
}
test
public static void main(String[] args) {
StudentDao studentDao = new StudentDao();
Student stu = new Student("李四","男",18);
//添加
//studentDao.addStu(stu);
//修改
stu.setName("张三");
stu.setAge(17);
studentDao.editStu(stu);
//查询所有
ArrayList list = studentDao.getList();
for (Student student : list) {
System.out.println(student);
}
//删除
studentDao.deleteStu("张三");
}
----结果----
mysql驱动加载成功!
修改成功
Student [id=3, name=李四, sex=男, age=18]
Student [id=4, name=张三, sex=男, age=17]
删除成功
DAO 一般使用 PreparedStatement 进行CRUD ,可防止sql注入
//使用 PreparedStatement 进行CRUD 可防止sql注入
public void addPreStu(Student stu) throws SQLException{
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "insert into student(name,sex,age) values(?,?,?)"; //未知值暂时用?代替
try {
conn = DbUtil.getConn();
//创建PreparedStatement对象:prepareStatement()
pstmt = conn.prepareStatement(sql);
//给?赋值, 从 1 开始
pstmt.setString(1, stu.getName());
pstmt.setString(2, stu.getSex());
pstmt.setInt(3, stu.getAge());
//执行指令
pstmt.executeUpdate();
System.out.println("添加成功!");
} catch (SQLException e) {
System.err.println("添加失败!" + sql);
e.printStackTrace();
}finally{
DbUtil.close(null, pstmt, conn);
}
}
public static void main(String[] args) throws SQLException {
StudentDao studentDao = new StudentDao();
Student stu = new Student("王五","女",18);
studentDao.addPreStu(stu);
}
----结果----
mysql驱动加载成功!
添加成功!
五、CRUD批量操作, 原理一样,实例 批量增加记录
Statement 批量添加
// Statement 批量添加
public void insertBatch(ArrayList list) throws SQLException{
Connection conn = null;
Statement stmt = null;
String sql = null;
try {
conn = DbUtil.getConn();
stmt = conn.createStatement();
for(Student stu: list){
sql = "insert into student(name,sex,age) values('"+stu.getName()+"','"+stu.getSex()+"',"+stu.getAge()+")";
stmt.addBatch(sql); //将指令添加到批处理
}
stmt.executeBatch(); //批量执行
System.out.println("添加成功");
} catch (SQLException e) {
System.err.println("添加失败!"+sql);
e.printStackTrace();
} finally{
DbUtil.close(null, stmt, conn);
}
}
PreparedStatement 批量添加
// PreparedStatement 批量添加
public void insertBatch(ArrayList list) {
Connection conn = null;
PreparedStatement pstmt = null;
String sql ="insert into student(name,sex,age) values(?,?,?)";
try {
conn = DbUtil.getConn();
pstmt = conn.prepareStatement(sql);
for (Student stu : list) {
pstmt.setString(1, stu.getName());
pstmt.setString(2, stu.getSex());
pstmt.setInt(3, stu.getAge());
//将指令添加到批处理
pstmt.addBatch();
}
pstmt.executeBatch(); //批量执行
System.out.println("添加成功");
} catch (SQLException e) {
System.err.println("添加失败!"+sql);
e.printStackTrace();
} finally{
DbUtil.close(null, pstmt, conn);
}
}
测试:
StudentDao studentDao = new StudentDao();
//模拟数据
ArrayList list = new ArrayList();
list.add(new Student("张三","女",20));
list.add(new Student("李四","男",22));
list.add(new Student("王五","男",23));
studentDao.insertBatch(list);
----结果----
mysql驱动加载成功!
添加成功!
事务的基本概念:事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功
Connection 接口中提供了:
setAutoCommit(), // 设置数据库自动提交模式,改为手动提交
commit(), rollback() ...
//DAO 转账 采用事务处理
public void tansfer(int fromId,int toId,double money){
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "update card set money = money + ? where id = ?";
try {
conn = DbUtil.getConn();
pstmt = conn.prepareStatement(sql);
conn.setAutoCommit(false); //设置数据库自动提交模式,改为手动提交
//转出的卡
pstmt.setDouble(1, -money);
pstmt.setInt(2, fromId);
pstmt.addBatch(); //加入批处理
//转入的卡
pstmt.setDouble(1, money);
pstmt.setInt(2, toId);
pstmt.addBatch(); //加入批处理
int[] num = pstmt.executeBatch(); //返回值表示各条指令执行后数据表中受影响的记录条数
if(num[0]!=1 || num[1]!=1){
conn.rollback(); //回滚:撤销事务
conn.setAutoCommit(true); //恢复自动提交
throw new DaoException("转账失败,卡号不存在!");
}
conn.commit(); //手动提交数据
conn.setAutoCommit(true); //恢复自动提交
System.out.println("转账成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Test 类
CarDao carDao = new CarDao();
carDao.tansfer(1001, 1002, 4000);
----结果----
mysql驱动加载成功!
转账成功
七、JDBC 调用存储过程和函数
在数据库中我们可以写一些存储过程和函数,来封装一些常用的SQL语句,存储过程和函数目的是为了可重复地执行操作数据库的sql语句的集合
主要区别就是函数必须有返回值(return),并且函数的参数只有 IN 类型而存储过程有 IN、OUT、INOUT 这三种类型。
- 存储过程的返回值,可以有多个值
- 函数的返回值,只有一个值
函数是可以嵌入在SQL中使用的,可以在SELECT等SQL语句中调用,而存储过程则不行。我们可以在数据库中创建一些常用的存储过程和函数,这样我们在数据访问层直接调用即可。这里记录一下使用JDBC调用存储过程和函数的方法
1. JDBC调用存储过程和函数
步骤: 1 通过 Connection 对象的 prepareCall() 方法创建一个 CallableStatement 对象的实例。在使用 Connection 对象的 prepareCall()方法时,需要传入一个String类型的字符串,该字符串用于指明如何调用存储过程 函数: {?= call [(,, ...)]} 存过: {call [(,, ...)]}
2 通过 CallableStatement 对象的 registerOutParameter() 方法注册 OUT 参数或者返回值参数类型 3 通过 CallableStatement 对象的 setXxx() 方法设定 IN 或 INOUT参数 若想将参数默认值设为 Null ,可以使用 setNull() 方法 4 通过 CallableStatement 对象的 execute() 方法执行存储过程 5 如果所调用的是带返回参数的存储过程,还需要通过 CallableStatement 对象的 getXxx() 方法获取其 OUT 或者返回值的值
2. 存储过程数据库操作(Navicat 操作)
2.1 调用无参存储过程
存过:{call [(,, ...)]}
drop procedure if exists p_student_select;
create procedure p_student_select()
begin
select * from student;
end
DAO:
public List procedure(){
List list = new ArrayList();
Connection conn = null;
CallableStatement call = null;
ResultSet rs = null;
try {
conn = DbUtil.getConn();
call = conn.prepareCall("{call p_student_select()}"); //调用存过
rs = call.executeQuery(); //执行存过
while(rs.next()) {
//创建Student对象存放记录的值
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setSex(rs.getString("sex"));
stu.setAge(rs.getInt("age"));
list.add(stu);
}
System.out.println("存过调用成功!");
} catch (SQLException e) {
System.out.println("存过调用失败!");
e.printStackTrace();
} finally{
DbUtil.close(null, call, conn);
}
return list;
}
----test类----
List list = (ArrayList) studentDao.procedure();
for (Student student : list) {
System.out.println(student);
}
----结果----
存过调用成功!
Student [id=3, name=李四, sex=男, age=18]
Student [id=4, name=李四, sex=男, age=18]
Student [id=29, name=张三, sex=女, age=20]
Student [id=30, name=李四, sex=男, age=22]
Student [id=31, name=王五, sex=男, age=23]
2.2 调用入参的存储过程
存过:
drop procedure if exists p_student_insert;
create procedure p_student_insert(IN p_name varchar(30), IN p_sex varchar(1))
begin
insert into student(name,sex) values(p_name,p_sex);
end
DAO:
public void procedure(String name, String sex) {
Connection conn = null;
CallableStatement call = null;
try {
conn = DbUtil.getConn();
call = conn.prepareCall("{call p_student_insert(?,?)}"); //调用存过
call.setString(1, name);
call.setString(2, sex);
call.execute(); //执行存过
System.out.println("存过调用成功!");
} catch (SQLException e) {
System.out.println("存过调用失败!");
e.printStackTrace();
} finally{
DbUtil.close(null, call, conn);
}
}
----test类----
studentDao.procedure("存过", "男");
----结果----
存过调用成功!
2.3 调用出参存储过程
存过:
drop procedure if exists p_student_count;
DELIMITER $$
create procedure p_student_count(INOUT p_name varchar(30), OUT p_count Integer(11))
begin
select count(*) into p_count from student where name = p_name;
end
$$
DELIMITER ;
DAO:
public Integer procedure(String name) {
Connection conn = null;
CallableStatement call = null;
Integer count = null;
try {
conn = DbUtil.getConn();
call = conn.prepareCall("{call p_student_count(?,?)}");
call.setString(1, name);
call.registerOutParameter(2, java.sql.Types.INTEGER); //注册存储过程的out型参数类型;使用之前必须注册
call.execute();
count = call.getInt(2); //获得存储过程的输出参数
System.out.println("存过调用成功!");
} catch (SQLException e) {
System.out.println("存过调用失败!");
e.printStackTrace();
} finally{
DbUtil.close(null, call, conn);
}
return count;
}
----结果----
Integer count = studentDao.procedure("李四");
System.out.println(count);
----
mysql驱动加载成功!
存过调用成功!
3
3. 函数 数据库操作(Navicat 操作)
函数: {?= call [(,, ...)]}
drop function if exists f_student_select;
DELIMITER $$
create function f_student_select(p_name varchar(30))
returns VARCHAR(30)
begin
DECLARE p_count int;
select count(*) into p_count from student where name = p_name;
return CONCAT("函数返回记录数为: ",p_count);
end
$$
DELIMITER ;
DAO:
public String function(String name) {
Connection conn = null;
CallableStatement call = null;
String result = null;
try {
conn = DbUtil.getConn();
call = conn.prepareCall("{?= call f_student_select(?)}");
call.registerOutParameter(1, java.sql.Types.VARCHAR); //注册函数的返回值参数类型;使用之前必须注册
call.setString(2, name);
call.execute();
result = call.getString(1); //获得函数的返回值
System.out.println("存过调用成功!");
} catch (SQLException e) {
System.out.println("存过调用失败!");
e.printStackTrace();
} finally{
DbUtil.close(null, call, conn);
}
return result;
}
----test----
String result = studentDao.function("李四");
System.out.println(result);
----结果----
mysql驱动加载成功!
存过调用成功!
函数返回记录数为: 3