文章目录
JDBC简述
- JDBC(Java Data Base Connectivity,java数据库连接)是SUN公司开发的,一种用于执行SQL语句的Java API
- JDBC是Java访问数据库的标准规范,可以为不同的关系型数据库提供统一访问,它由一组用Java语言编写的接口和类组成
- JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信
- mysql的驱动
mysql-connector-java-5.1.37-bin.jar
JDBC原理
下载jar包,登录mybatis
mybatis主包,还有以来的jar包。
ORM:概念 ,
Mybatis是ORM的一个实现/Hibernate
orm可以是的开发人员 像操作对象一样 操作数据库表。
开发mybatis程序从步骤:
1.配置mybatis
conf.xml:配置数据库信息 和 需要加载的映射文件
表 - 类
映射文件xxMapper.xml :增删改查标签
测试类:
session.selectOne(“需要查询的SQL的namespace.id”,“SQL的参数值”);
JDBC访问数据库流程:
- 加载数据库驱动---------------------一
- 创建并获取数据库链接----------------二
- 创建jdbc statement对象-------------------三
- 设置sql语句
- 设置sql语句中的参数(使用preparedStatement)
- 通过statement执行sql并获取结果-------------四
- 对sql执行结果进行解析处理
- 释放资源(resultSet、preparedstatement、connection)
1、加载JDBC驱动程序:
Driver接口
– Driver接口由数据库厂家提供,对于java开发者而言,只需要使用 Driver接口就可以了。
– 在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。不 同的数据库有不同的装载方法。
– 驱动:就是各个数据库厂商实现的Sun公司提出的JDBC接口。 即对 Connection等接口的实现类的jar文件 。通过初始化驱动类com.mysql.jdbc.Driver装载为驱动,该类就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle数据库那么该驱动类将不同。
在连接数据库之前,首先要加载想要连接的数据库的驱动到 JVM(Java虚拟机),
这通过java.lang.Class类的静态方法forName(String className)实现。
– 装载MySql驱动 Class.forName("com.mysql.jdbc.Driver");
– 装载Oracle驱动 Class.forName("oracle.jdbc.driver.OracleDriver");
//如果没有导入成功jdbc的jar包,会抛出ClassNotFoundException异常,所以我们try catch
try{
Class.forName("com.mysql.jdbc.Driver"); //加载MySql的驱动类
}catch(ClassNotFoundException e){
System.out.println("找不到驱动程序类 ,加载驱动失败!");
e.printStackTrace() ;
}
执行Class.forName("com.mysql.jdbc.Driver");
会加载JDBC驱动。
成功加载后,会将Driver类的实例注册到DriverManager类(驱动管理类)中。
DriverManager接口 :
-
DriverManager是JDBC的管理层,作用于用户和驱动程序之间。
-
DriverManager跟踪可用的驱动程序,并在数据库和相应的驱动程序 之间建立连接。
2. JDBC连接数据库
•要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象, 该对象就代表一个数据库的连接。
Connection与特定数据库的连接(会话),在连接上下文中执行 SQL 语句并返回结果。
Connection获取方式:使用DriverManager类的
DriverManager.getConnectin(String url , String username , String password )
方法传入指定的欲连接的数据库的路径、数据库的用户名和密码来获得Connection接口 。
– 连接MYSQL数据库:
Connection con = DriverManager.getConnection("jdbc:mysql://host:port/database","user", "password");
– 连接ORACLE数据库:
Connection con = DriverManager.getConnection("jdbc:oracle:thin:@host:port:databse","us er","password");
例如:
String url = "jdbc:mysql://localhost:3306/test" ;
String username = "root" ;
String password = "root" ;
try{
Connection con =
DriverManager.getConnection(url , username , password ) ;
}catch(SQLException se){
System.out.println("数据库连接失败!");
se.printStackTrace() ;
}
获取连接的适合还可以指定编码格式
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");
3、创建一个preparedStatement
这步的意义是准备好sql语句,完成对数据库的增删改查。
连接数据库成功后,要执行SQL语句,必须获得java.sql.Statement
实例。把sql语句传给statement就获得了statement实例。
String sql = "insert into t_user (username,pwd,regTime) values (?,?,?)"; //?占位符,可以在下一步中填入
PreparedStatement ps = conn.prepareStatement(sql);
con对象后面可以调用三种方法:对应下面的返回值
返回值Statement实例分为以下3种类型:
- Statement: 由createStatement创建,用于发送简单的SQL语句。(不带参数的)(有漏洞,一般不用) 。执行静态SQL语句。
- PreparedStatement: 继承自Statement接口,由prepareStatement创建,用于发送含有一个或多个输入参数的sql语句。PreparedStatement对象比Statement对象的效率更 高,并且可以【防止SQL注入】。我们一般都用PreparedStatement.。执行动态SQL语句。
- CallableStatement: 继承自PreparedStatement 。由方法prePareCall创建,用于调用存储过程。 执行数据库存储过程
因为语句中有占位符没填入数据,所以我们可以使用set方法填入数据。
String sql = "insert into t_user (username,pwd,regTime) values (?,?,?)"; //?占位符,可以在下一步中填入
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,"用户");
ps.setInt(2,"123456");
ps.setFloat(3, "1.23");
4、执行SQL语句
Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate
和execute
常用的Statement类方法(执行mysql语句):
ResultSet executeQuery(String sqlString)
:执行查询数据库的SQL语句 ,返回一个结果集(ResultSet)对象。int executeUpdate(String sqlString)
:用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等。返回更新的行数。boolean execute(sqlString)
:用于执行返回多个结果集、多个更新计数或二者组合的语句,返回是否有结果集。
具体实现的代码:
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ; //返回结果集,后面常用
int rows = stmt.executeUpdate("INSERT INTO ...") ;
boolean flag = stmt.execute(String sql) ;
5、遍历结果集
两种情况:
1、执行更新返回的是本次操作影响到的记录数。
2、执行查询返回的结果是一个ResultSet对象。
ResultSet接口: Statement执行SQL语句时返回ResultSet结果集。
– ResultSet提供的检索不同类型字段的方法,常用的有:
- getString():获得在数据库里是varchar、char等数据类型的对象。
- getFloat():获得在 数据库里是Float类型的对象。
- getDate():获得在数据库里面是Date类型的数据。
- getBoolean():获得在数据库里面是Boolean类型的数据
while(rs.next()){
String name = rs.getString("name") ;
String pass = rs.getString(1) ; // 此方法比较高效
}
(列是从左到右编号的,并且从列1开始)
6、关闭JDBC对象资源
操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声明顺序相反:依序关闭使用之对象及连接: ResultSet — Statement — Connection
1、先关闭requestSet
2、再关闭preparedStatement
3、最后关闭连接对象connection
if(rs != null){ // 关闭记录集
try{
rs.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(stmt != null){ // 关闭声明 preparedStatement
try{
stmt.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(conn != null){ // 关闭连接对象
try{
conn.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
完整代码:
package com.iot.mybatis.jdbc;
//import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Created by Administrator on 2016/2/21.
*/
public class JdbcTest {
public static void main(String[] args) {
//数据库连接
Connection connection = null;
//预编译的Statement,使用预编译的Statement提高数据库性能
PreparedStatement preparedStatement = null;
//结果集
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://120.25.162.238:3306/mybatis001?characterEncoding=utf-8", "root", "123");
//定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
//获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
问题总结
1.数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。
设想:使用数据库连接池管理数据库连接。
2.将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3.向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
4.从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。(Mybatis解决)
JDBC其他内容
批处理
– Batch – 对于大量的批处理,建议使用Statement,因为PreparedStatement的预编译空间有限 ,当数据量特别大时,会发生异常。
package com.bjsxt.jdbc;
// 测试批处理的基本用法
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo05 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//加载驱动类
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","123456");
conn.setAutoCommit(false); //设为手动提交
long start = System.currentTimeMillis();
stmt = conn.createStatement();
for(int i=0;i<20000;i++){
stmt.addBatch("insert into t_user (username,pwd,regTime) values ('gao"+i+"',666666,now())");
}
stmt.executeBatch();
conn.commit(); //提交事务
long end = System.currentTimeMillis();
System.out.println("插入20000条数据,耗时(毫秒):"+(end-start));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
//遵循:resultset-->statment-->connection这样的关闭顺序!一定要将三个trycatch块,分开写!
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt!=null){
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
事务
事务基本概念
在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
事务:一组要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元!
事务的四大特点(ACID)
- atomicity(原子性):表示一个事务内的所有操作是一个整体,要么全部成功,要么全失败;
- consistency(一致性) :表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态;
- isolation(隔离性):事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态, 要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。
- durability(持久性):持久性事务完成之后,它对于系统的影响是永久性的。
Mysql中的事务
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
- 开启事务:start transaction
- 结束事务:commit或rollback
在执行SQL语句之前,先执行start transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所作出的影响会持久到数据库中,或者rollback,表示回滚到事务的起点,之前做的所有操作都被撤销了。
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 1000.00 |
| 2 | ls | 1000.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE account SET balance=900 WHERE name = 'zs';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 900.00 |
| 2 | ls | 1000.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> UPDATE account SET balance=1100 WHERE name = 'ls';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 900.00 |
| 2 | ls | 1100.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 1000.00 |
| 2 | ls | 1000.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE account SET balance=balance-100 WHERE name = 'zs';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 900.00 |
| 2 | ls | 1000.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> UPDATE account SET balance=balance+100 WHERE name = 'ls';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 900.00 |
| 2 | ls | 1100.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
| 1 | zs | 900.00 |
| 2 | ls | 1100.00 |
| 3 | ww | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)
JDBC事务
在JDBC中处理事务,都是通过Connection完成的。
同一事务中所有的操作,都在使用同一个Connection对象。
①JDBC中的事务
Connection的三个方法与事务有关:
- setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值为true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置为false,那么相当于开启了事务了;con.setAutoCommit(false) 表示开启事务。
- commit():提交结束事务。
- rollback():回滚结束事务。
1、事务开始于:
- 连接到数据库上,并执行一条DML语句(INSERT、UPDATE或DELETE)。
- 前一个事务结束后,又输入了另外一条DML语句。
2、事务结束于:
- 执行COMMIT或ROLLBACK语句。
- 执行一条DDL语句,例如CREATE TABLE语句;在这种情况下,会自动执 行COMMIT语句。
- 执行一条DCL语句,例如GRANT语句;在这种情况下,会自动执行 COMMIT语句。
- 断开与数据库的连接。
- 执行了一条DML语句,该语句却失败了;在这种情况中,会为这个无效的 DML语句执行ROLLBACK语句。
JDBC处理事务的代码格式
try{
con.setAutoCommit(false);//开启事务
......
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}
public class AccountDao {
/*
* 修改指定用户的余额
* */
public void updateBalance(Connection con, String name,double balance) {
try {
String sql = "UPDATE account SET balance=balance+? WHERE name=?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setDouble(1,balance);
pstmt.setString(2,name);
pstmt.executeUpdate();
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
import cn.itcast.jdbc.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo1 {
/*
* 演示转账方法
* 所有对Connect的操作都在Service层进行的处理
* 把所有connection的操作隐藏起来,这需要使用自定义的小工具(day19_1)
* */
public void transferAccounts(String from,String to,double money) {
//对事务的操作
Connection con = null;
try{
con = JdbcUtils.getConnection();
con.setAutoCommit(false);
AccountDao dao = new AccountDao();
dao.updateBalance(con,from,-money);//给from减去相应金额
if (true){
throw new RuntimeException("不好意思,转账失败");
}
dao.updateBalance(con,to,+money);//给to加上相应金额
//提交事务
con.commit();
} catch (Exception e) {
try {
con.rollback();
} catch (SQLException e1) {
e.printStackTrace();
}
throw new RuntimeException(e);
}
}
@Test
public void fun1() {
transferAccounts("zs","ls",100);
}
}
事务隔离级别从低到高:
– 读取未提交(Read Uncommitted)
– 读取已提交(Read Committed)
– 可重复读(Repeatable Read)
– 序列化(serializable)
package com.bjsxt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试事务的基本概念和用法
* @author 高淇 www.sxt.cn
*
*/
public class Demo06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
try {
//加载驱动类
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","123456");
conn.setAutoCommit(false); //JDBC中默认是true,自动提交事务
ps1 = conn.prepareStatement("insert into t_user (username,pwd) values (?,?)");
ps1.setObject(1, "高淇");
ps1.setObject(2, "123456");
ps1.execute();
System.out.println("插入一个用户,高淇");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ps2 = conn.prepareStatement("insert into t_user (username,pwd) values (?,?,?)");
ps2.setObject(1, "马士兵");
ps2.setObject(2, "123456");
ps2.execute();
System.out.println("插入一个用户,马士兵");
conn.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(ps1!=null){
ps1.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
五、事务隔离级别
1、事务的并发读问题
- 脏读:读取到另外一个事务未提交数据(不允许出来的事);
- 不可重复读:两次读取不一致;
- 幻读(虚读):读到另一事务已提交数据。
2、并发事务问题
因为并发事务导致的问题大致有5类,其中两类是更新问题三类是读问题。
- 脏读(dirty read):读到另一个事务的未提交新数据,即读取到了脏数据;
- 不可重复读(unrepeatable):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;
- 幻读(虚读)(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录。
3、四大隔离级别
4个等级的事务隔离级别,在相同的数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。
1、SERIALIZABLE(串行化)
- 不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
- 性能最差
2、REPEATABLE READ(可重复读)(MySQL)
- 防止脏读和不可重复读,不能处理幻读
- 性能比SERIALIZABLE好
3、READ COMMITTED(读已提交数据)(Oracle)
- 防止脏读,不能处理不可重复读和幻读;
- 性能比REPEATABLE READ好
4、READ UNCOMMITTED(读未提交数据)
- 可能出现任何事物并发问题,什么都不处理。
- 性能最好
六、MySQL隔离级别
MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:
SELECT @@
TX_ISOLATION
;
也可以通过下面语句来设置当前连接的隔离级别:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4选1]
七、JDBC设置隔离级别
con.setTransactionIsolation(int level) :参数可选值如下:
- Connection.TRANSACTION_READ_UNCOMMITTED;
- Connection.TRANSACTION_READ_COMMITTED;
- Connection.TRANSACTION_REPEATABLE_READ;
- Connection.TRANSACTION_READ_SERIALIZABLE。JDBV