JDBC 看过 没用

jdbc&连接池&事务

预备知识:

一、数据库的基本知识

1. 数据库概述

l 什么是数据库

数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作。

l 什么是数据库管理系统

数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中表内的数据。

l 数据库与数据库管理系统的关系

 

2. 数据库表

数据库中以表为组织单位存储数据。

表类似我们的Java类,每个字段都有对应的数据类型。

那么用我们熟悉的java程序来与关系型数据对比,就会发现以下对应关系。

类----------表

类中属性----------表中字段

对象----------记录

 

3. 表数据

根据表字段所规定的数据类型,我们可以向其中填入一条条的数据,而表中的每条数据类似类的实例对象。表中的一行一行的信息我们称之为记录。

 

l 表记录与java类对象的对应关系

 

4. 常见数据库

l 常见的数据库管理系统

 

MYSQL:开源免费的数据库,小型的数据库.已经被Oracle收购了.MySQL6.x版本也开始收费。

Oracle:收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。

DB2 :IBM公司的数据库产品,收费的。常应用在银行系统中.

SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。

SyBase:已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。

SQLite:嵌入式的小型数据库,应用在手机端。

 

常用数据库:MYSQL,Oracle.

这里使用MySQL数据库。MySQL中可以有多个数据库,数据库是真正存储数据的地方。

二、windows中安装mysql

见 <<MySQL安装图解.pdf>>

三、JDBC

1. JDBC概述

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API。JDBC是Java访问数据库的标准规范,可以为不同的关系型数据库提供统一访问,它由一组用Java语言编写的接口和类组成。

JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。 今天我们使用的是mysql的驱动mysql-connector-java-5.1.37-bin.jar

 

JDBC规范(掌握四个核心对象):

1) DriverManager:用于注册驱动

2) Connection: 表示与数据库创建的连接

3) Statement: 操作数据库sql语句的对象

4) ResultSet: 结果集或一张虚拟表

2. jdbc原理

Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。

 

JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。

3. Jdbc入门案例

1) 准备数据

之前我们学习了sql语句的使用,并创建的分类表category,今天我们将使用JDBC对分类表进行增删改查操作

#创建数据库

create database day04;

#使用数据库

use day04;

#创建分类表

create table category(

cid int PRIMARY KEY AUTO_INCREMENT  ,

   cname varchar(100)

);

#初始化数据

insert into category (cname) values('家电');

insert into category (cname) values('服饰');

insert into category (cname) values('化妆品');

2) 导入驱动jar包

创建lib目录,存放mysql的驱动mysql-connector-java-5.1.37-bin.jar

选中mysql的jar包,右键选择“ Add as Library...” 完成jar导入

 

3) 代码演示

操作步骤:

1.注册驱动.

2.获得连接.

3.获得执行sql语句的对象

4.执行sql语句,并返回结果

5.处理结果

6.释放资源.

 

//查询所有的分类信息

@Test

public void demo1() throws Exception{

// 注意:使用JDBC规范,采用都是 java.sql包下的内容

//1 注册驱动

Class.forName("com.mysql.jdbc.Driver");

//2 获得连接

String url = "jdbc:mysql://localhost:3306/mydb";

Connection conn = DriverManager.getConnection(url, "root", "root");

//3获得执行sql语句的对象

Statement stmt = conn.createStatement();

//4执行SQL语句

ResultSet rs = stmt.executeQuery("select * from category");

 

//5处理结果集

while(rs.next()){

// 获得一行数据

Integer cid = rs.getInt("cid");

String cname = rs.getString("cname");

System.out.println(cid + " , " + cname);

}

//6释放资源

rs.close();

stmt.close();

conn.close();

}

4. API详解

l 1) 注册驱动

DriverManager.registerDriver(new com.mysql.jdbc.Driver())

不建议使用,原因有2个:

导致驱动被注册2次

强烈依赖数据库的驱动jar

解决办法:

Class.forName("com.mysql.jdbc.Driver");

l 2) 获取链接

static Connection getConnection(String url, String user, String password)试图建立到给定数据库 URL 的连接。

参数说明

  1. url 需要连接数据库的位置(网址)
  2. user 用户名
  3. password 密码

扩展说明:

URL:SUN公司与数据库厂商之间的一种协议。

jdbc:mysql://localhost:3306/day04

协议:子协议:/ IP :端口号数据库  

mysql: jdbc:mysql://localhost:3306/day04 或者 jdbc:mysql:///day04(默认本机连接) ​

oracle数据库: jdbc:oracle:thin:@localhost:1521:sid

 

l 3) java.sql.Connection接口: 连接对象

接口的实现在数据库驱动中。所有与数据库交互都是基于连接对象的

Statement createStatement();//创建操作sql语句的对象

l 4)java.sql.Statement 接口:操作sql语句, 并返回相应结果

String sql = "某SQL语句";

获取Statement语句执行平台:

Statement stmt =con.createStatement();

 

常用方法:

int executeUpdate(String sql);--执行insert update delete语句.

ResultSet executeQuery(String sql); --执行select语句.

boolean execute(String sql); --仅当执行select并且有结果时才返回true,执行其他的语句返回false.

 

l 5)处理结果集 :resultSet

注意 : 执行 insert、update、delete无需处理结果集

ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法来获取指定列的数据:(与数组索引从0开始不同,这里索引从1开始)

rs.next();//指向第一行

rs.getInt(1);//获取第一行第一列的数据

 

获取数据常用方法:

Object getObject(int index) / Object getObject(String name) 获得任意对象

String getString(int index) / String getString(String name) 获得字符串

int getInt(int index) / int getInt(String name) 获得整形

double getDouble(int index) / double getDouble(String name)获得双精度浮点型

 

l 6)释放资源

与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。

rs.close();

stmt.close();

con.close();

 

5. Jdbc工具类

“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。

public class JdbcUtils {

 

private static String driver = "com.mysql.jdbc.Driver";

private static String url = "jdbc:mysql://localhost:3306/webdb_4";

private static String user = "root";

private static String password = "root";

 

static{

try {

//注册驱动

Class.forName(driver);

} catch (Exception e) {

throw new RuntimeException(e);

}

 

}

 

/**

 * 获得连接

 * @return

 * @throws SQLException

 */

public static Connection getConnection() throws  SQLException{

//获得连接

Connection conn = DriverManager.getConnection(url, user, password);

return conn;

}

 

/**

 * 释放资源

 * @param conn

 * @param st

 * @param rs

 */

public static void closeResource(Connection conn , Statement st , ResultSet rs){

 

if(rs != null){

try {

rs.close();

} catch (SQLException e) {

}

}

 

if(st != null){

try {

st.close();

} catch (SQLException e) {

}

}

 

if(conn != null){

try {

conn.close();

} catch (SQLException e) {

}

}

 

}

}

 

6. Jdbc增删改查操作

l 插入操作

@Test

public void demo01(){

//添加

 

Connection conn = null;

Statement st = null;

ResultSet rs = null;

 

try {

//1 获得连接

conn = JdbcUtils.getConnection();

 

//操作

//1) 获得语句执行者

st = conn.createStatement();

//2) 执行sql语句

int r = st.executeUpdate("insert into category(cname) values('测试')");

 

//3) 处理结果

System.out.println(r);

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//释放资源

JdbcUtils.closeResource(conn, st, rs);

}

}

 

l 修改操作

@Test

public void demo02(){

//修改

Connection conn = null;

Statement st = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

st = conn.createStatement();

int r = st.executeUpdate("update category set cname='测试2' where cid = 4");

System.out.println(r);

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, st, rs);

}

}

l 删除

@Test

public void demo03(){

//删除

Connection conn = null;

Statement st = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

//操作

st = conn.createStatement();

int r = st.executeUpdate("delete from category where cid = 4");

System.out.println(r);

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, st, rs);

}

 

}

l 通过id查询详情

@Test

public void demo04(){

//通过id查询详情

Connection conn = null;

Statement st = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

//操作

st = conn.createStatement();

rs = st.executeQuery("select * from category where cid = 30");

 

if(rs.next()){

String cid = rs.getString("cid");

String cname = rs.getString("cname");

System.out.println(cid + " @ " + cname );

} else {

System.out.println("没有数据");

}

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, st, rs);

}

}

l 查询所有

@Test

public void demo05(){

//查询所有

Connection conn = null;

Statement st = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

//操作

st = conn.createStatement();

rs = st.executeQuery("select * from category");

 

while(rs.next()){

String cid = rs.getString("cid");

String cname = rs.getString("cname");

System.out.println(cid + " @ " + cname );

}

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, st, rs);

}

}

四、预编译执行平台

1. SQL注入问题

SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。

假设有登录SQL语句如下:

SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;

此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a时,则真正执行的代码变为:

SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’  OR ’a’=’a’;

此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。 为此,我们使用PreparedStatement来解决对应的问题。

7. API详解:预处理对象

preparedStatement:预编译对象,是Statement对象的子类。

 

特点:

l 性能高

l 会把SQL语句先编译

l 能过滤掉用户输入的关键词

 

PreparedStatement预处理对象,处理的每条sql语句中所有的实际参数,都必须使用占位符?替换。

String sql = "select * from user where username = ? and password = ?";

PreparedStatement使用,需要通过以下3步骤完成:

  1. PreparedStatement预处理对象代码:

// 获得预处理对象,需要提供已经使用占位符处理后的SQL语句

PreparedStatement psmt = conn.prepareStatement(sql)

  1. 设置实际参数

void setXxx(int index, Xxx xx) 将指定参数设置指定类型的值

参数1:index 实际参数序列号,从1开始。

参数2:xxx 实际参数值,xxx表示具体的类型。

例如:

setString(2, "1234") 把SQL语句中第2个位置的占位符?替换成实际参数 "1234"

  1. 执行SQL语句

int executeUpdate(); --执行insert update delete语句.

ResultSet executeQuery(); --执行select语句.

boolean execute(); --执行select返回true 执行其他的语句返回false.

8. 基于预处理实现CURD操作

l 1) 插入操作

@Test

public void demo01(){

//添加:向分类表中添加数据

Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

 

try {

//1 获得连接

conn = JdbcUtils.getConnection();

//2 处理sql语句

String sql = "insert into category(cname) values(? )";

//3获得预处理对象

psmt = conn.prepareStatement(sql);

//4设置实际参数

psmt.setString(1,"预处理");

//5执行

int r = psmt.executeUpdate();

 

System.out.println(r);

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

//6释放资源

JdbcUtils.closeResource(conn, psmt, rs);

}

}

l 2) 更新操作

@Test

public void demo02(){

//修改

Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

//1 sql语句

String sql = "update category set cname = ? where cid = ?";

//2 获得预处理对象

psmt = conn.prepareStatement(sql);

//3设置实际参数

psmt.setString(1, "测试数据");

psmt.setInt(2, 4);

//4执行

int r = psmt.executeUpdate();

System.out.println(r);

 

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, psmt, rs);

}

}

l 3) 通过id查询详情

@Test

public void demo05(){

//通过id查询

Connection conn = null;

PreparedStatement psmt = null;

ResultSet rs = null;

 

try {

conn = JdbcUtils.getConnection();

 

String sql = "select * from category where cid = ?";

psmt = conn.prepareStatement(sql);

psmt.setInt(1, 2);

rs = psmt.executeQuery();

if(rs.next()){

System.out.println("查询到");

} else {

System.out.println("查询不到");

}

 

 

} catch (Exception e) {

throw new RuntimeException(e);

} finally{

JdbcUtils.closeResource(conn, psmt, rs);

}

}

五、使用连接池重写工具类

1. 连接池原理

连接池理解为存放多个连接的集合。

 

 

使用连接池技术的目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。

9. 编写标准的数据源(规范)

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池:C3P0、DRUID。

10. C3P0连接池工具类实现

C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用C3P0连接池需要导入jar包,c3p0使用时还需要添加配置文件“c3p0-config.xml”

 

使用步骤:

添加jar包

编写配置文件 c3p0-config.xml,放在src中(注:文件名一定不要写错)

编写工具类

l 编写配置文件 c3p0-config.xml

<c3p0-config>

   <!-- 使用默认的配置读取连接池对象 -->

   <default-config>

     <!--  连接参数 -->

     <property name="driverClass">com.mysql.jdbc.Driver</property>

     <property name="jdbcUrl">jdbc:mysql://localhost:3306/day03</property>

     <property name="user">root</property>

     <property name="password">root</property>

 

     <!-- 连接池参数 -->

     <property name="initialPoolSize">5</property>

     <property name="maxPoolSize">10</property>

     <property name="checkoutTimeout">2000</property>

     <property name="maxIdleTime">1000</property>

   </default-config>

 </c3p0-config>

说明: c3p0 连接池常用的配置参数说明:

initialPoolSize : 初始连接数  刚创建好连接池的时候准备的连接数量

maxPoolSize : 最大连接数 连接池中最多可以放多少个连接

checkoutTimeout : 最大等待时间 连接池中没有连接时最长等待时间

maxIdleTime : 最大空闲回收时间 连接池中的空闲连接多久没有使用就会回收

l 编写C3P0工具类

public class JdbcUtils {

    //创建一个C3P0的连接池对象(使用c3p0-config.xml中default-config标签中对应的参数)

    public static DataSource ds = new ComboPooledDataSource();

 

    //从池中获得一个连接

    public static Connection getConnection() throws SQLException {

        return ds.getConnection();

    }

 

    //释放资源

    public static void closeAll(ResultSet rs, Statement stmt, Connection conn){

        if (rs != null) {

            try {

                rs.close();

            } catch (SQLException e) {

                throw new RuntimeException(e);

            }

            rs = null;

        }

        if (stmt != null) {

            try {

                stmt.close();

            } catch (SQLException e) {

                throw new RuntimeException(e);

            }

            stmt = null;

        }

        if (conn != null) {

            try {

                conn.close();

            } catch (SQLException e) {

                throw new RuntimeException(e);

            }

            conn = null;

        }

    }

}

l C3p0连接池工具类的使用

public class Demo {

public static void main(String[] args) throws Exception {

// 拿到连接

Connection conn = JdbcUtils.getConnection();

 

// 执行sql语句

String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, "李四");

pstmt.setInt(2, 30);

pstmt.setDouble(3, 50);

int i = pstmt.executeUpdate();

System.out.println("影响的函数: " + i);

 

// 关闭资源

JdbcUtils.close(conn, pstmt);

}

}

六、事务操作

事务概述

事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.

事务作用:保证在一个事务中多次SQL操作要么全都成功,要么全都失败. 安全性,完整性

1. Mysql事务操作

sql语句

描述

start transaction

开启事务

commit

提交事务

rollback

回滚事务

l 准备数据

# 创建一个表:账户表.

create database webdb;

# 使用数据库

use webdb;

# 创建账户表

create table account(

id int primary key auto_increment,

name varchar(20),

money double

);

# 初始化数据

insert into account values (null,'jack',10000);

insert into account values (null,'rose',10000);

insert into account values (null,'tom',10000);

l 操作

n MYSQL中可以有两种方式进行事务的管理

u 自动提交:MySql默认自动提交。及执行一条sql语句提交一次事务。

u 手动提交:先开启,再提交

l 方式1:手动提交

start transaction;

update account set money=money-1000 where name='jack';

update account set money=money+1000 where name='rose';

commit;

#或者

rollback;

l 方式2:自动提交,通过修改mysql全局变量“autocommit”进行控制

11. 事务特征: ACID

原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性(Consistency)事务前后数据的完整性必须保持一致。

隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

12. 并发访问问题

如果不考虑隔离性,事务存在3中并发访问问题。

l 脏读:一个事务读到了另一个事务未提交的数据.

l 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。

l 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。

13. 隔离级别: 解决问题

数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。

read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

a) 存在:3个问题(脏读、不可重复读、虚读)。

b) 解决:0个问题

read committed 读已提交,一个事务读到另一个事务已经提交的数据。

a) 存在:2个问题(不可重复读、虚读)。

b) 解决:1个问题(脏读)

repeatable read:可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。

a) 存在:1个问题(虚读)。

b) 解决:2个问题(脏读、不可重复读)

serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

a) 存在:0个问题。

b) 解决:3个问题(脏读、不可重复读、虚读)

安全和性能对比

安全性:serializable > repeatable read > read committed > read uncommitted

性能 : serializable < repeatable read < read committed < read uncommitted

常见数据库的默认隔离级别:

MySql:repeatable read

Oracle:read committed

14. Jdbc事务操作

Connection 对象的方法名

描述

conn.setAutoCommit(false)

开启事务

conn.commit()

提交事务

conn.rollback()

回滚事务

 

代码操作:

public class Demo01JDBC {

    public static void main(String[] args) {

        Connection conn = null;

        Statement stat = null;

        try {

            //1.注册驱动

            Class.forName("com.mysql.jdbc.Driver");

            //2.获取数据库连接对象Connection

            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day04", "root", "root");

 

            //开启事务

            conn.setAutoCommit(false);

 

        //3.获取执行sql语句的执行者对象Statement

        stat = conn.createStatement();

        //4.执行sql语句,获取结果

        int row1 = stat.executeUpdate("update account set money=money-1000 where name='jack';");

        System.out.println(0/0);

        int row2 = stat.executeUpdate("update account set money=money+1000 where name='rose';");

        //5.处理结果

        if(row1>0 && row2>0){

            System.out.println("转账成功!");

 

            //提交事务

            conn.commit();

        }

    } catch (Exception e) {

        e.printStackTrace();

        System.out.println("转账失败!");

 

        //回滚事务

        try {

            conn.rollback();

        } catch (SQLException e1) {

            e1.printStackTrace();

        }

    } finally {

        //6.释放资源

        if(stat!=null){

            try {

                stat.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

        if(conn!=null){

            try {

                conn.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

    }

}

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/shan13936/p/13904377.html