JDBC
JDBC
概念:Java DataBase Connectivity。是sun公司定义的一套操作所有关系型数据库的接口,各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
使用
步骤:
- 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
2.右键-->Add As Library - 注册驱动
Class.forName("com.mysql.jdbc.Driver");
加载驱动jar包中的Driver类,class对象会自动调用Driver类中的静态代码块 - 获取数据库连接对象 Connection
- 定义sql
- 获取sql语句的执行对象Statement
- 执行sql,接受返回结果
- 处理结果
- 释放资源
注意:idea中使用数据库先要在右侧边栏中开启database
-
示例代码
package com.shenguopin.main; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class jdbctest { public static void main(String[] args) throws Exception { //导入驱动jar包 //注册驱动,mysql5之后的驱动jar包可以省略这步 Class.forName("com.mysql.jdbc.Driver"); //DriverManager.registerDriver(new Driver()); 这样也行 //获取数据库连接对象 Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root"); //获取执行SQL的对象 Statement stmt = con.createStatement(); //执行SQL //定义sql语句 String sql = "SELECT * FROM user"; ResultSet rs = stmt.executeQuery(sql); //处理结果 while(rs.next()) { System.out.println(rs.getInt("id") + "\t" + rs.getString("name")); } //释放资源 stmt.close(); con.close(); rs.close(); } }
各个类详解
-
DriverManager:驱动管理对象
-
注册驱动:告诉程序该使用哪一个数据库驱动jar
Class.forName("com.mysql.jdbc.Driver");此语句会加载要使用的com.mysql.jdbc.Driver类,并自动调用该类中的静态代码块:static { try { java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");} }
静态代码块调用了DriverManager的registerDriver方法:static void registerDriver(Driver driver) :注册给定的驱动程序。
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
-
获取数据库连接:
-
方法:static Connection getConnection(String url, String user, String password)
-
url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子:jdbc:mysql://localhost:3306/db3
简写:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
-
-
-
Connection:数据库连接对象
- 功能:
- 获取执行sql 的对象
- 普通执行者对象:Statement createStatement()
- 预编译执行者对象:PreparedStatement prepareStatement(String sql)
- 管理事务:
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
- 提交事务:void commit()
- 回滚事务:void rollback()
- 释放资源:
- void close()
- 获取执行sql 的对象
- 功能:
-
Statement:执行sql的对象
- 执行sql
- boolean execute(String sql) :可以执行任意的sql
- 执行增、删、改语句:int executeUpdate(String sql)
返回值是影响的行数,如果返回值>0的则执行成功 - 执行DQL(select)语句:ResultSet executeQuery(String sql)
返回值ResultSet是一个表。
注意:sql语句中进行字符串拼接非常麻烦:
String insert = "insert into student values(1,'李白',23)";//必须把字符串用引号括起来 //字符串变量要这么写 String name = "李白"。 String sql = "insert into student values(1,'"+name+"',23)";
- 执行sql
-
ResultSet:结果集对象,封装查询结果
- 判断结果集中是否还有数据:boolean next()
有数据返回true,并将索引向下移动一行。没有数据返回false - XXX getXxx(参数):获取数据
- Xxx:代表数据类型。如:int getInt(),String getString()
- 参数:
- int:代表列的编号,从1开始,如:getString(1)
- String:代表列名称。如:getDouble("balance")
- 判断结果集中是否还有数据:boolean next()
-
PreparedStatement:执行sql的对象
-
SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
- 输入用户随便,输入密码:aaa' or 'a' = 'a
- sql:select * from user where username = 'fhdsjkf' and password = 'aaa' or 'a' = 'a'
-
解决sql注入问题:使用PreparedStatement对象来解决
-
预编译的SQL:参数使用?作为占位符,同时解决了sql拼接的麻烦。
-
步骤:
- 定义sql:注意:sql的参数使用 ?作为占位符。
如:select * from user where username = ? and password = ?; - 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
- 给?赋值:
- 方法: setXxx(参数1,参数2)
- 参数1:?的位置编号 从1 开始
- 参数2:?的值
- 方法: setXxx(参数1,参数2)
- 定义sql:注意:sql的参数使用 ?作为占位符。
-
注意:后期都会使用PreparedStatement来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
- 处理bolb等类型的数据
String sql = "insert into user(username,photo) values('zs', 图片字节流)"; //SQL中有blob类型的数据,可以保存图片,但是不能在命令行中插入图片,只能用jdbc操作
- 获取元数据:就是表头的数据
String sql = "select * from student"; PreparedStatement pstmt = connection.PrepareStatement(sql); ResultSetMetaData metaData = pstmt.getMetaData(); int count = metaData.getColumnCount();//获取字段数量 for(int i = 0; i < count; i++){ System.out.println(metaData.getColumnName(i + 1)); }
- 获取自增长的键值
/* * 我们通过JDBC往数据库的表格中添加一条记录,其中有一个字段是自增的,如果获取这个自增的值呢? * PreparedStatement是Statement的子接口。 * Statement接口中有一些常量值:Statement.RETURN_GENERATED_KEYS。保存了自增的值 * 获取自增的key值的步骤: * (1)PreparedStatement pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); * (2) 执行sql语句。 * (3)执行完成sql语句后,通过PreparedStatement的对象调用getGeneratedKeys()方法来获取自增长键值,遍历结果集 * ResultSet rs = pst.getGeneratedKeys(); */
- 批处理
/* * 批处理:批量处理sql。可以节约大量时间 * 例如:一次性向表中添加10000条数据 * 如何进行批处理操作? * (1)在url中要加一个参数 * rewriteBatchedStatements=true * 那么我们的url就变成了:jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true * (2)将要一起执行的预编译语句组成一批,然后调用executeBatch()。 * pst.addBatch(); * int[] all = pst.executeBatch(); * 注意:如果批量添加时,insert使用values,不要使用value */ public class TestBatch { public static void main(String[] args) throws Exception{ long start = System.currentTimeMillis(); Class.forName("com.mysql.jdbc.Driver"); //获取连接,把批处理的命令加入其中 String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"; Connection conn = DriverManager.getConnection(url, "root", "123456"); String sql = "insert into t_department values(null,?,?)"; PreparedStatement pst = conn.prepareStatement(sql); //添加10000数据 for (int i = 1; i <=1000; i++) { pst.setObject(1, "模拟部门"+i); pst.setObject(2, "模拟部门的简介"+i); pst.addBatch();//添加到批处理一组操作中,攒一块处理 /*if(i % 500 == 0){//有时候也攒一部分,执行一部分 //2.执行 pst.executeBatch(); //3.清空 pst.clearBatch(); }*/ } pst.executeBatch(); //4、关闭 pst.close(); conn.close(); long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start));//耗时:821 } }
-
示例代码
package com.shenguopin.main; import java.sql.*; public class JDBCDemo05 { public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt = null; ResultSet resultSet = null; try { //注册驱动 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //建立连接 conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root"); //写sql语句 String sql = "update account set balance=? where id=?"; //获取sql语句的PrepareStatement执行对象 pstmt = conn.prepareStatement(sql); //给?赋值 pstmt.setDouble(1,5000); pstmt.setInt(2,6); //执行sql语句,不用再传参数了,前面已经传过了 int count = pstmt.executeUpdate(); System.out.println(count); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { if(pstmt!=null){ try { pstmt.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } }
-
案例
通过jdbc对学生表进行crud操作。
类的层级:
- DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后在Spring的配置文件中定义此接口的实现类,然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类,显得结构非常清晰,DAO层的数据源配置,以及有关数据库连接的参数都在Spring的配置文件中进行配置
- Service层:Service层主要负责业务模块的逻辑应用设计。同样是首先设计接口,再设计其实现的类,接着再Spring的配置文件中配置其实现的关联。这样我们就可以在应用中调用Service接口来进行业务处理。Service层的业务实现,具体要调用到已定义的DAO层的接口,封装Service层的业务逻辑有利于通用的业务逻辑的独立性和重复利用性,程序显得非常简洁。
- Controller层:Controller层负责具体的业务模块流程的控制,在此层里面要调用Serice层的接口来控制业务流程,控制的配置也同样是在Spring的配置文件里面进行,针对具体的业务流程,会有不同的控制器,我们具体的设计过程中可以将流程进行抽象归纳,设计出可以重复利用的子单元流程模块,这样不仅使程序结构变得清晰,也大大减少了代码量。
数据库和数据表
-- 创建db14数据库
CREATE DATABASE db14;
-- 使用db14数据库
USE db14;
-- 创建student表
CREATE TABLE student(
sid INT PRIMARY KEY AUTO_INCREMENT, -- 学生id
NAME VARCHAR(20), -- 学生姓名
age INT, -- 学生年龄
birthday DATE -- 学生生日
);
-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),(NULL,'王五',25,'1996-06-06'),(NULL,'赵六',26,'1994-10-20');
实体类
- Student类,成员变量对应表中的列
- 注意:所有的基本数据类型需要使用包装类,以防null值无法赋值
package com.itheima02.domain;
import java.util.Date;
public class Student {
private Integer sid;
private String name;
private Integer age;
private Date birthday;
public Student() {
}
public Student(Integer sid, String name, Integer age, Date birthday) {
this.sid = sid;
this.name = name;
this.age = age;
this.birthday = birthday;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
Dao:
package com.example.Dao;
import com.example.domain.Student;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
/**
* @author shenguopin
* @data 2021/8/11 19:29
*/
public class StudentDaoImpl implements StudentDao{
@Override
public ArrayList<Student> findAll() {
ArrayList<Student> list = new ArrayList<>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
//3.获取执行对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "select * from student";
rs = stmt.executeQuery(sql);
//5.处理结果集
while(rs.next()){
int id = rs.getInt("sid");
String name = rs.getString("name");
int age = rs.getInt("age");
Date birthday = rs.getDate("birthday");
Student student = new Student(id,name,age,birthday);
list.add(student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return list;
}
@Override
public Student findById(Integer sid) {
Student stu = null;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
//3.获取执行对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "select * from student where sid ='"+sid+"'";
rs = stmt.executeQuery(sql);
//5.处理结果集
while(rs.next()){
int id = rs.getInt("sid");
String name = rs.getString("name");
int age = rs.getInt("age");
Date birthday = rs.getDate("birthday");
stu = new Student(id,name,age,birthday);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return stu;
}
@Override
public int insert(Student stu) {
Connection conn = null;
Statement stmt = null;
int result = 0;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
//3.获取执行对象
stmt = conn.createStatement();
//4.执行sql语句
Integer sid = stu.getSid();
String name = stu.getName();
Integer age = stu.getAge();
Date d = stu.getBirthday();//日期类需要格式化
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(d);
//注意insert语句的写法,需要把sid等变量全部拼接成字符串才行
String sql = "insert into student values('"+sid+"','"+name+"','"+age+"','"+birthday+"')";
result = stmt.executeUpdate(sql);
//5.处理结果集
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return result;
}
@Override
public int delete(Integer id) {
Connection conn = null;
Statement stmt = null;
int result = 0;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
//3.获取执行对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "delete from student where sid = "+id;
result = stmt.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return result;
}
@Override
public int update(Student stu) {
Connection conn = null;
Statement stmt = null;
int result = 0;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
//3.获取执行对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "update student set sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+stu.getBirthday()+"' where sid = '"+stu.getSid()+"'";
result = stmt.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return result;
}
}
Service:
package com.example.service;
import com.example.Dao.StudentDao;
import com.example.Dao.StudentDaoImpl;
import com.example.domain.Student;
import java.util.ArrayList;
/**
* @author shenguopin
* @date 2021/8/11 19:53
*/
public class StudentServiceImpl implements StudentService{
private StudentDao dao = new StudentDaoImpl();
@Override
public ArrayList<Student> findAll() {
return dao.findAll();
}
@Override
public Student findById(Integer sid) {
return dao.findById(sid);
}
@Override
public int insert(Student stu) {
return dao.insert(stu);
}
@Override
public int delete(Integer id) {
return dao.delete(id);
}
@Override
public int update(Student stu) {
return dao.update(stu);
}
}
controller
package com.example.controller;
import com.example.domain.Student;
import com.example.service.StudentService;
import com.example.service.StudentServiceImpl;
import org.junit.Test;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Date;
/**
* @author shenguopin
* @date 2021/8/11 20:47
*/
public class StudentController {
private StudentService service = new StudentServiceImpl();
//查询所有学生信息
@Test
public void findAll(){
ArrayList<Student> list = service.findAll();
for (Student student : list) {
System.out.println(student);
}
}
//条件查询
@Test
public void findById(){
Student stu = service.findById(3);
System.out.println(stu);
}
//添加学生信息
@Test
public void insert(){
Student stu = new Student(5,"周七",27,new Date());
int result = service.insert(stu);
if(result!=0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}
//删除学生信息
@Test
public void delete(){
int result = service.delete(5);
if(result!=0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
//修改学生信息
@Test
public void update(){
Student stu = service.findById(5);
stu.setName("周七七");
int result = service.update(stu);
if(result!=0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}
}
JDBCUtils工具类
工具类代码
/**
* JDBC工具类,简化驱动设置,获取连接,释放资源等重复操作
*/
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* @author shenguopin
* @date 2021/9/8 22:03
*/
public class JDBCUtils {
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal();//这个类可以在同一个线程中共享变量
private JDBCUtils(){}
//类加载的时候自动读取数据库连接的配置信息
static{
try{
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(is);
dataSource = DruidDataSourceFactory.createDataSource(properties);
}catch (Exception e) {
e.printStackTrace();
}
}
//返回数据库连接的方法
public static Connection getConnection(){
Connection connection = threadLocal.get();//优先使用同一个线程中共享的变量
if(connection == null){//如果同一线程中没有共享的连接
try {
connection = dataSource.getConnection();
threadLocal.set(connection);//添加一个共享连接
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
//关闭连接的方法
public static void close(Connection connection, Statement stmt, ResultSet rs){
if(connection != null){
try {
connection.close();
threadLocal.remove();//清除掉线程中共享的连接
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection connection,Statement stmt){
close(connection,stmt,null);
}
//通用的增删改方法
public static int CommonUpdate(String sql,Object...objs){
int result = 0;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = dataSource.getConnection();
pstmt = connection.prepareStatement(sql);
for(int i = 0; i < objs.length; i ++){
pstmt.setObject(i+1,objs[i]);
}
result = pstmt.executeUpdate();
ResultSetMetaData metaData = pstmt.getMetaData();
int count = metaData.getColumnCount();
for(int i = 0; i < count; i++){
System.out.println(metaData.getColumnName(i + 1));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
close(connection,pstmt,null);
}
return result;
}
//查询的方法,返回的结果包装成对象列表
public static <T> List<T> get(Class<T> cls, String sql,Object...args) throws InstantiationException, IllegalAccessException {
List<T> list = new ArrayList<>();
Connection connection = JDBCUtils.getConnection();
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
pstmt = connection.prepareStatement(sql);
//设置sql的参数
if(args != null && args.length > 0){
for(int i = 0; i < args.length; i ++){
pstmt.setObject(i+1,args[i]);
}
}
//获取结果集
resultSet = pstmt.executeQuery();
//获取元数据
ResultSetMetaData metaData = pstmt.getMetaData();
int columnCount = metaData.getColumnCount();
//遍历结果集
while(resultSet.next()){
T t = cls.newInstance();
for(int i = 0; i < columnCount; i ++){
//获取字段名
String columnLabel = metaData.getColumnLabel(i + 1);
//根据字段名获取属性并为对象赋值
Field field = cls.getDeclaredField(columnLabel);
Object value = resultSet.getObject(columnLabel);
field.setAccessible(true);//private成员都需要这一步
field.set(t,value);
}
//将对象添加到列表
list.add(t);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return list;
}
}
## JDBC控制事务
1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
2. 操作:
1. 开启事务
2. 提交事务
3. 回滚事务
3. 使用Connection对象来管理事务
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务
* 提交事务:commit()
* 当所有sql都执行完提交事务
* 回滚事务:rollback()
* 如果出现了错误,就在catch中回滚事务
4. 示例代码
```java
//jdbc事务操作
public class JDBCDemo06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//获取连接
conn = JDBCUtils.getConnection();
//开始事务
conn.setAutoCommit(false);
//sql语句
//张三向李四转账500
String sql1 = "UPDATE account set balance=balance - ? WHERE id = ?";
String sql2 = "UPDATE account set balance=balance + ? WHERE id = ?";
//创建sql语句的执行对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//设置?的参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//执行sql
pstmt1.executeUpdate();
//手动制造异常
int i = 1/0;
pstmt2.executeUpdate();
//如果程序无错误,会执行下面的提交事务
conn.commit();
} catch (Exception e){
try {
if(conn!=null){
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);//这里用到了多态,PrepareStatement是Statement的子类
JDBCUtils.close(pstmt2,conn);
}
}
}
JDBC连接池
-
概念:其实就是一个容器(集合),存放数据库连接对象。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
-
好处:
- 节约资源
- 用户访问高效
-
实现:
-
标准接口:javax.sql包下的DataSource
- 方法:
- 获取连接:getConnection()
- 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
- 方法:
-
一般我们不去实现它,有数据库厂商来实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供的
-
-
C3P0:数据库连接池技术
-
步骤:
- 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,注意:不要忘记导入数据库驱动jar包
- 定义配置文件:
名称:c3p0.properties 或者 c3p0-config.xml
路径:直接将文件放在src目录下即可 - 创建核心对象,数据库连接池对象ComboPooledDataSource
- 获取连接:getConnection
-
代码:
package dataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; //c3po连接池演示 public class C3P0Demo { public static void main(String[] args) { //1.创建数据库连接池对象 DataSource ds = new ComboPooledDataSource(); //2.获取连接池对象,最多可以获取maxPoolSize(默认10个)个连接对象 for (int i = 0; i < 11; i++) { Connection conn = null; try { conn = ds.getConnection(); } catch (SQLException throwables) { throwables.printStackTrace(); } System.out.println(i+":"+conn); } }
-
-
Druid:数据库连接池实现技术,由阿里巴巴提供的
-
步骤
- 导入jar包 druid-1.0.9.jar
- 定义配置文件:
是properties形式的,名称随意,目录随意 - 手动加载配置文件
- 获取数据库连接池对象:通过工厂来来获取DruidDataSourceFactory
- 获取连接:getConnection
-
代码
package dataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.util.Properties; public class DruidDemo { public static void main(String[] args) throws Exception { //导入jar包和配置文件 //加载配置文件 Properties prop = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); prop.load(is); //获取连接池对象 DataSource ds = DruidDataSourceFactory.createDataSource(prop); //获取连接 Connection conn = ds.getConnection(); System.out.println(conn); /*DruidDataSource ds = new DruidDataSource(); *基本配置 *ds.setUsername("root"); *ds.setPassword("root"); *ds.setUrl("jdbc:mysql:///test"); *ds.setDriverClassName("com.mysql.jdbc.Driver"); *连接池配置 *ds.setInitialSize(5);初始化5条连接 *ds.setMaxActive(10);最大10条连接 *ds.setMaxWait(2000);最长等待时间 *DruidPooledConnection conn = ds.getConnection(); */ } }
-
定义工具类DruidJDBCUtils
-
提供静态代码块加载配置文件,初始化连接池对象
-
提供方法
- 获取连接池的方法
- 获取连接对象的方法
- 释放资源
-
示例代码
/* 数据库连接池工具类 */ public class DataSourceUtils { //1.私有构造方法 private DataSourceUtils(){} //2.定义DataSource数据源变量 private static DataSource dataSource; //3.提供静态代码块,完成配置文件的加载和获取连接池对象 static { try{ //加载配置文件 InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties"); Properties prop = new Properties(); prop.load(is); //获取数据库连接池对象 dataSource = DruidDataSourceFactory.createDataSource(prop); } catch(Exception e) { e.printStackTrace(); } } //4.提供获取数据库连接的方法 public static Connection getConnection() { Connection con = null; try { con = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return con; } //5.提供获取数据库连接池的方法 public static DataSource getDataSource() { return dataSource; } //6.提供释放资源的方法 public static void close(Connection con, Statement stat, ResultSet rs) { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close(Connection con, Statement stat) { close(con,stat,null); } }
-
-
Spring JDBC
连接池简化了连接对象的创建,工具类有进一步简化了,但是编写sql语句的步骤还是有些麻烦。Sprint框架提供的JDBCTemplate类可以对JDBC进一步简化,尤其是在编写sql语句时非常方便。
步骤
- 导入jar包
- 创建JdbcTemplate对象。依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(ds); - 调用JdbcTemplate的方法来完成对数据库的操作
- update()
- queryForMap()将结果集封装为map集合,只能用在单行的结果集上,列名为Key,值为value
- queryForList():将结果集封装为list集合,每一条记录是一个Map,再将Map装到List中
- query():将结果封装为JavaBean对象
参数:RowMapper接口
Spring提供的实现类:BeanPropertyRowMapper
new BeanPropertyRowMapper<类名>(类名.class) - queryForObject:查询结果封装成对象,一般用于聚合函数的查询
package cn.itcast.jdbctemplate;
import cn.itcast.domain.Emp;
import cn.itcast.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class JdbcTemplateDemo2 {
//Junit单元测试,可以让方法独立执行
//1. 获取JDBCTemplate对象
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 1. 修改1号数据的 salary 为 10000
*/
@Test
public void test1(){
//2. 定义sql
String sql = "update emp set salary = 10000 where id = 1001";
//3. 执行sql
int count = template.update(sql);
System.out.println(count);
}
/**
* 2. 添加一条记录
*/
@Test
public void test2(){
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = template.update(sql, 1015, "郭靖", 10);
System.out.println(count);
}
/**
* 3.删除刚才添加的记录
*/
@Test
public void test3(){
String sql = "delete from emp where id = ?";
int count = template.update(sql, 1015);
System.out.println(count);
}
/**
* 4.查询id为1001的记录,将其封装为Map集合
* 注意:这个方法查询的结果集长度只能是1
*/
@Test
public void test4(){
String sql = "select * from emp where id = ? or id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001,1002);
System.out.println(map);
//{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
}
/**
* 5. 查询所有记录,将其封装为List
*/
@Test
public void test5(){
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
/**
* 6. 查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6(){
String sql = "select * from emp";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
return emp;
}
});
for (Emp emp : list) {
System.out.println(emp);
}
}
/**
* 6. 查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6_2(){
String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp : list) {
System.out.println(emp);
}
}
/**
* 7. 查询总记录数
*/
@Test
public void test7(){
String sql = "select count(id) from emp";
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}
Apache的DBUtils
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
其中QueryRunner类封装了SQL的执行,是线程安全的。
(1)可以实现增、删、改、查、批处理、
(2)考虑了事务处理需要共用Connection。
(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
(1)更新
public int update(Connection conn, String sql, Object... params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
......
(2)插入
public
....
(3)批处理
public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
public
.....
(4)使用QueryRunner类实现查询
public Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
....
ResultSetHandler接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet rs)该方法的返回值将作为QueryRunner类的query()方法的返回值。
该接口有如下实现类可以使用:
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ScalarHandler:查询单个值对象
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。