JAVA教程:JDBC和事务

一、JDBC

JDBC是一套Java连接数据库的标准。标准就是接口,接口定了标准(规则 == 抽象的方法),接口没有具体的实现。具体的实现由数据库厂商提供的驱动来实现。也就是说,JDBC指定了规范,但是要连接MySQL数据库,得使用的是MySQL数据体提供的驱动程序。又比如,要连接Oracle数据库,得使用Oracle数据库提供的驱动程序来实现。总结:驱动才是对JDBC的具体的实现。

二、JDBC初体验

1.如何引入第三方类库(.jar文件)

2.使用jdbc来连接数据库

// 1.注册数据库的驱动

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

// DriverManager.registerDriver(new com.oracle.jdbc.Driver());

// 2.获得连接数据库的连接对象

String url = "jdbc:mysql://localhost:3306/db_190302";// 地址

// http://www.baidu.com

String user = "root";

String password = "123456";

// Connection对象就是一个连接对象

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

// 3.获得一个能够执行sql语句的Statement对象

Statement stmt = conn.createStatement();

// 4.执行sql语句

String sql = "select * from emp";

// 5.获得查询生成的结果集

ResultSet rs = stmt.executeQuery(sql);

// 6.遍历结果集

while (rs.next()) {

System.out.println(rs.getObject(1) + "," + rs.getObject(2) + "," + rs.getObject(3) + "," + rs.getObject(4)

+ "," + rs.getObject(5) + "," + rs.getObject(6));

}

//7.释放连接

conn.close();

stmt.close();

rs.close();

三、JDBC详解

JDBC是一套规范,这套规范定义在java.sql包下。是规范了java程序连接数据库的步骤(标准):

注册驱动

获得连接对象

获得执行sql语句的对象 Statement

执行sql语句获得结果集对象 ResultSet

操作结果集对象

关闭资源

1.注册驱动

DriverManager.registDriver(new com.mysql.jdbc.Driver)——这种方式驱动会被注册两遍,因为在Driver类中有一个静态代码块(随着类的加载而被执行)中已经将此驱动注册。

因此使用这种方式注册驱动: Class.forName("com.mysql.jdbc.Driver ");——只加载该类,让该类的静态代码块被执行,做注册驱动的效果。

2.获得连接对象

1)getConnection(String url,String user,String password)

(1)url和uri的区别

url: 统一资源定位符 帮助我们定位一个资源,是一个资源的路径。

http://www.baidu.com

ftp://29.38.29.11

jdbc:msyql://29.38.44.11:3306/mydb

uri:

d:/1.jpg

c:/meinv.avi

(2)mysql中的url该怎么写

写法一:

jdbc:mysql://数据库所在主机的ip地址:数据库的端口号/具体的数据库名

写法二:

jdbc:mysql:// 数据库所在主机的ip地址:数据库的端口号/具体的数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC

写法三:

jdbc:mysql:///具体的数据库名

这种方式默认使用 localhost:3306

(3)user

用户名,默认是root

(4)password

密码

2)getConnection(String url)

String url = "jdbc:mysql:///db_190302?user=root&password=123456"

3)getConnection(String url,Properties p)

Properties对象是一个属性(键值对)集对象,可以从静态的配置文件中获得已有的属性集。

Properties p = new Properties();

p.setProperty("user", "root");

p.setProperty("password", "123456");

Connection conn = DriverManager.getConnection(url, p);

升级版:

Properties从静态配置文件中获得配置的好处在于: 如果配置的信息有修改,那么只要修改配置文件即可,而不用修改程序本身。

3.Statement对象

通过Connetion的createStatement()方法来获得。

这是一个执行sql语句的对象。

1) ResultSet executeQuery(String sql)

执行查询: 是用于执行select这一类的sql语句的。

返回一个封装着此次查询产生结果的对象。

2) executeUpdate(String sql)

执行:增删改的sql语句, insert delete update

3)boolean execute(String sql)

可以执行select也可以执行DML(增删改)或DDL

如果执行的select的sql语句,返回的是true

如果执行的是DML或DDL,返回的是false

根据返回的布尔值,调用不同的方法来获得结果,如果是true,调用getResultSet来获得查询得到的结果集,如果是false,调用getUpdateCount()获得受影响的行数

4.JDBC工具类的设计

public class MyJDBCUtil {

static String url;

static String user;

static String password;

//复用性!

static {

//随着类的加载就注册驱动

Properties p = new Properties();

try {

//1.获得静态配置文件中的信息

p.load(new FileInputStream("db.properties"));

String driverClass = p.getProperty("driverClass");

//2.给参数赋值

url = p.getProperty("url");

user = p.getProperty("user");

password = p.getProperty("password");

//3.注册驱动

Class.forName(driverClass);

} catch (ClassNotFoundException | IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 释放资源的方法

* @param conn 连接对象

* @param stmt 执行sql语句的对象

* @param rs 结果集对象

* @throws SQLException

*/

public static void releaseResource(Connection conn,Statement stmt,ResultSet rs) throws SQLException {

if(rs!=null) {

rs.close();

}

if(stmt!=null) {

stmt.close();

}

if(conn!=null) {

conn.close();

}

}

/**

* 获得连接对象的方法

* @return

* @throws SQLException

*/

public static Connection getConnection() throws SQLException {

return DriverManager.getConnection(url, user, password);

}

/**

* 执行任意类型的sql语句的方法

* @param sql 增删改查都可以

* @throws IOException

* @throws FileNotFoundException

*/

public static void showResult(String sql) throws Exception {

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

Statement stmt = conn.createStatement();

//2.执行sql语句 获得结果

boolean b = stmt.execute(sql);

ResultSet rs = null;

if(b) {

//sql是select

//就会有结果集

rs = stmt.getResultSet();

//

System.out.println("执行select语句,结果集遍历如下:");

while(rs.next()) {

System.out.println(rs.getObject(1)+","+rs.getObject(2));

}

}else {

//sql是增删改或ddl

int rows = stmt.getUpdateCount();

System.out.println("执行DML或DDL语句,受影响的行数为"+rows);

}

//3.释放资源

if(rs!=null) {

rs.close();//如果rs是null,直接调用close方法会报空指针异常

}

stmt.close();

conn.close();

}

}

5.ResultSet对象

当执行select的查询语句时将会获得结果集,结果集被封装在ResultSet对象中

1) next()

遍历ResultSet中的数据的循环条件,刚开始位于第一行之前,第一次调用时就来到了第一行,以此类推

2)如何获得每一行中每一列的数据

(getInt getString getDate getDouble getXXX ...)

||

getObject

这些方法可以传两种参数

int columnindex ==> 传列的索引,索引从1开始

String columnlable ==> 传列名

while(rs.next()) {

// 部门编号(int) 部门名称(String) 部门地址(String)

//目前处于第一行

/*int deptno = rs.getInt(1);

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

String loc = rs.getString(3);

System.out.println(deptno+"\t"+dname+"\t"+loc);*/

Object deptno = rs.getObject(1);

Object dname = rs.getObject(2);

Object loc = rs.getObject(3);

System.out.println(deptno+"\t"+dname+"\t"+loc);

}

四、SQL注入问题

案例:

模拟登陆验证

步骤:

一、向数据库中存入用户名和密码

二、获得用户输入的用户名和密码

三、去数据库验证此用户名和密码是否正确

1)从工具类中获得数据库的连接对象。

2)获得执行sql语句的Statement对象

3)执行sql获得返回结果

sql怎么写? 如果输入的用户名和密码与数据库中某一条记录的用户名和密码完全相同。则输入正确;否则输入错误。

4)判断rs.next()的结果,如果是true 就表示登录成功,否则登录失败。

1.SQL注入问题的出现

我们发现当输入这样的数据时,参数会被作为sql语句本身来执行,而非是纯粹的参数,因此这种情况非常危险,这种情况也称为sql注入问题。

2.解决SQL注入问题

通过PreparedStatement对象来解决。在创建PreparedStatement对象时会先把sql语句预编译进体内。

然后再为sql语句中占位符“?”赋值。

那么此时,为?赋值的参数内容,只能作为参数本身,而不会影响到sql语句。这样就防止了sql注入问题。

注意: 设置完pstmt对象的参数后,还要执行。

五、事务

案例:

小陈和小林

小陈问小林借钱,去银行转账。 ATM转账 小林-1000 ===||||===> 小陈+1000

如果在转出 和转入 两条sql操作中出现了问题,那么就会导致转出成功,但转入失败。这是不应该发生的。

因此通过事务来解决这样的问题。

1.什么是事务

事务是sql中最小的操作单位。默认情况下,一条sql语句是一个单独的事务。当一个事务被执行,就会立即生效。因此我们可以让多条sql语句成为一个单独的事务,当事务被执行,多条sql语句同时生效,也可以多条sql语句同时不生效。

2.事务的操作——如何让多条sql语句成为一个单独的事务

1)开启事务

在MySQL中: start transaction;

2)结束事务

(1)提交 commit; 让开启事务到提交事务之间的所有sql语句都生效

(2)回滚 rollback; 让开启事务到回滚事务之间的所有sql语句都不生效。让事务回滚到开启事务的那一刻。(也可以设置回滚点,让事务回滚到指定回滚点。)

3.在jdbc中操作事务

conn.setAutoCommit(false) ==>开启事务

结束事务:

- 提交事务: conn.commit()

- 回滚事务: conn.rollback();

/**

* 实现转账的方法

* @param out_account 转出账号

* @param in_account 转入账号

* @param amount 转账金额

* @return 是否成功

* @throws Exception

*/

public static boolean transfer(String out_account, String in_account, double amount) throws Exception {

//获得连接对象

Connection conn = MyJDBCUtil.getConnection();

//开启事务

conn.setAutoCommit(false);

try {

//转出

String sql = "update tb_account set amount=amount-? where name=?";

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setDouble(1,amount);

pstmt.setString(2, out_account);

pstmt.executeUpdate();

int i=10/0;

//转入

String sql1 = "update tb_account set amount=amount+? where name=?";

PreparedStatement pstmt1 = conn.prepareStatement(sql1);

pstmt1.setDouble(1, amount);

pstmt1.setString(2, in_account);

pstmt1.executeUpdate();

conn.commit();//提交事务

} catch (Exception e) {

conn.rollback();//只要出现异常。就回滚!

return false;//转账失败

}

return true;//转账成功

}

扩展:

/**

* 实现转账的方法,无论什么情况转账都会成功!

* @param out_account 转出账号

* @param in_account 转入账号

* @param amount 转账金额

* @return 是否成功

* @throws Exception

*/

public static boolean transferPro(String out_account, String in_account, double amount) throws Exception {

//获得连接对象

Connection conn = MyJDBCUtil.getConnection();

//开启事务

conn.setAutoCommit(false);

Savepoint sp = null;

try {

//转出

String sql = "update tb_account set amount=amount-? where name=?";

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setDouble(1,amount);

pstmt.setString(2, out_account);

pstmt.executeUpdate();

//=========设置了存档点===============

sp = conn.setSavepoint();

int i=10/0;

//转入

String sql1 = "update tb_account set amount=amount+? where name=?";

PreparedStatement pstmt1 = conn.prepareStatement(sql1);

pstmt1.setDouble(1, amount);

pstmt1.setString(2, in_account);

pstmt1.executeUpdate();

conn.commit();//提交事务

} catch (Exception e) {

//conn.rollback();//只要出现异常。就回滚!

conn.rollback(sp);

//继续做转账

//转入

String sql1 = "update tb_account set amount=amount+? where name=?";

PreparedStatement pstmt1 = conn.prepareStatement(sql1);

pstmt1.setDouble(1, amount);

pstmt1.setString(2, in_account);

pstmt1.executeUpdate();

conn.commit();//提交事务 把存档点之前的sql提交,同时提交此处的sql

return true;//转账成功

}

return true;//转账成功

}

六、事务的特点 ACID

1.原子性

事务是一个最小的操作单元,不可再被分割,也就是说事务中的sql语句要么同时生效,要么同时不生效。

2.一致性

事务中的数据在事务提交之前,或者回滚以后,是一致的。

3. 持久性

事务一旦提交,对数据的影响是持久的(写入到磁盘中)。

4.隔离性

一个事务,在并发访问的情况下,不同隔离级别会出现不同效果。

七、事务的隔离级别

一个事务,在并发访问的情况下,不同隔离级别会出现不同效果。

并发: 在同一时刻,有多个客户顿在操作同一张表。

1.read uncommitted

一个事务中读到了另一个事务并未提交的结果。这种隔离级别就会导致: 脏读、不可重复读、虚读(幻读)。

2.read committed——oracle的默认隔离级别

一个事务中能读到另一个事务已经提交的结果,但是不能读到另一个事务没有提交的结果。这种隔离级别解决了脏读问题。但出现了不可重复读和幻读的问题,

所谓不可重复读,就是不能重复读,一重复读数据就不一样。

所谓的幻读,在一个事务中,两次读到的数据的条数不相同。

3.repeatable read——mysql的默认隔离级别

一个事务中可以重复的读,每次读到的数据都是一样的,无论其他事务对数据进行怎样的操作(添加数据、修改数据、提交事务)。当前事务每次读到的数据内容都不会有变化。

这种隔离级别解决了: 脏读、不可重复读、幻读。

4.serializable 串行化

最严苛的隔离级别。将并行变成串行。效率非常低,没有使用场景。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 本文内容 1.什么是JDBC以及为什么要使用JDBC 2.JDBC核心API的讲解 3.使用JDBC核心API进行...
    Vincilovfang阅读 1,191评论 0 11
  • JDBC简介 SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。JDBC...
    奋斗的老王阅读 1,504评论 0 51
  • 本人的环境为Myeclipse10、MySQL5.7.15 本文包括:简介JDBC编程步骤打通数据库程序详解—Dr...
    廖少少阅读 3,923评论 7 39
  • java事务的处理 转 https://www.cnblogs.com/Bonker/p/5417967.html...
    小小的Jobs阅读 1,360评论 0 1
  • 文/二月龙女 高考那年,我遇到了我的“陈浩南”,学会了翘课、撒谎、抽烟、喝酒、以暴制暴,忘记了最初的梦想,伤害了我...
    二月龙女阅读 1,485评论 4 10