JDBC的事务⽀持
JDBC的事务⽀持
事务的概念
当⼀个业务需求涉及到N个DML操作时,这个业务(或者时N个DML操作)当成⼀个整体来处理。在处理的过程中,如果有失败或异常,我们要回到业务开始时。如果成功处理,我们再将数据持久化到磁盘中。这样⼀个过程我们称之为⼀个事务。事务具有原⼦性。不可切割。
总结:
事务指逻辑上的⼀组操作,组成这组操作的各个单元,要么全成功,要么全不成功。
关键字:
commit
rollback
savepoint
事务的特性(ACID)
原⼦性(Atomicity)
指事务是⼀个不可分割的⼯作单位,事务中的操作要么都发⽣,要么都不发⽣。
⼀致性(Consistency)
事务必须使数据库从⼀个⼀致性状态变换到另外⼀个⼀致性状态。转账前和转账后的总⾦额不变。
隔离性(Lsolation)
事务的隔离性是多个⽤户并发访问数据库时,数据库为每⼀个⽤户开启的事务,不能被其他事务的操作数据所⼲扰,多个并发事务之间要相互隔离。
持久性(Durability)
指⼀个事务⼀旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发⽣故障也不应该对其有任何影响
MySQL事务
- 默认情况下,MySQL每执⾏⼀条SQL语句,都是⼀个单独的事务。
- 如果需要在⼀个事务中包含多条SQL语句,那么需要开启事务和结束事务。
开启事务:start transaction;
结束事务:commit或rollback;
事务开始于
•连接到数据库上,并执⾏⼀条DML语句insert、update或delete
•前⼀个事务结束后,⼜输⼊了另⼀条DML语句
事务结束于
•执⾏commit或rollback语句。
•执⾏⼀条DDL语句,例如create table语句,在这种情况下,会⾃动执⾏commit语句。
•执⾏⼀条DDL语句,例如grant语句,在这种情况下,会⾃动执⾏commit。
•断开与数据库的连接
•执⾏了⼀条DML语句,该语句却失败了,在这种情况中,会为这个⽆效的DML语句执⾏rollback语句。
sql语句实现事务⽀持
1.回滚情况
START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
ROLLBACK;
2.提交情况
START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
COMMIT;
JDBC的事务⽀持(⼿动控制事务)
Connection.setAutoCommit(boolean flag):此⽅法可以取消事务的⾃动提交功能,值为false。
Connection.commit():进⾏事务提交。
Connection.rollback():进⾏事务回滚.
多事务的情况
总结:
数据库通过设置事务的隔离级别防⽌以上情况的发⽣
隔离机制
隔离机制分类
注意点
1.oracle的隔离级别是read committed
mysql的隔离级别是repeatable read
3.级别越⾼,性能越低,数据越安全
4.设置隔离级别必须在事务之前
mysql中隔离级别相关操作
查看当前的事务隔离级别:SELECT @@TX_ISOLATION;
更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 四个级别之⼀。
JDBC控制事务的隔离级别
Connection接⼝:
设置⽅法:
Connection.setTransactionIsolation (int level);
数据库连接池技术
连接池技术出现的原因
在与数据库连接过程中,会⾮常消耗内存,性能⼤打折扣。如果每次请求都去重新连接数据库。那么,宕机的⼏率很⾼。
连接池技术原理和优势
- 原理:
连接池对象在初始化阶段 ⼀次性创建N个连接对象,这些连接对象存储在连接池对象中。当有请求过来时,先从连接池中寻找空闲连接对象并使⽤,当使⽤完后,将连接对象归还给连接池,⽽不是真正意义上断开连接。
- 优势:
这样也可以满⾜成千上万个请求,解决建⽴数据库连接耗费资源和时间很多的问题,提⾼性能。
编写标准的数据源
⾃定义数据库连接池要实现javax.sql.DataSource接⼝,⼀般都叫数据源
代码实现
编写数据源时遇到的问题
描述:连接对象要放回池⼦,不能关闭.
编写数据源时的解决办法
a.装饰设计模式:使⽤频率很⾼
⽬的:改写已存在的类的某个⽅法或某些⽅法,装饰设计模式(包装模式)
⼝诀:
1、编写⼀个类,实现与被包装类相同的接⼝。(具备相同的⾏为)
2、定义⼀个被包装类类型的变量。
3、定义构造⽅法,把被包装类的对象注⼊,给被包装类变量赋值。
4、对于不需要改写的⽅法,调⽤原有的⽅法。
5、对于需要改写的⽅法,写⾃⼰的代码。
b.默认适配器:装饰设计模式⼀个变体
常⽤的连接池技术
- dbcp :是apache组织旗下的⼀个数据库连接池技术产品
- c3p0 :是⼀个开源的连接池技术
- druid :是阿⾥的数据库连接池技术
dbcp
资源jar包
commons-dbcp2-2.6.0.jar
commons-pool2-2.4.3.jar
commons-logging.jar
配置⽂件dbcp.properties
此配置⽂件请放在src⽬录下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/qianfeng
username=root
pwd=123456
maxTotal=50
maxIdle=10
minIdle=3
initialSize=5
maxWaitMillis=60000
DBUtildbcp类型的编写
package monrningwork02.util;
import org.apache.commons.dbcp.BasicDataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DBUtildbcp {
private static String driver;
private static String url;
private static String username;
private static String password;
private static int maxTotal;
private static int maxIdle;
private static int minIdle;
private static int initialSize;
private static long maxWaitMillis;
//声明⼀个dbcp连接池变量
private static BasicDataSource pool;
static{
try{
pool = new BasicDataSource();//连接池对象
//使⽤类加载器中提供的⽅法来获取字节流对象,同时指定配置⽂件
InputStream is = DBUtildbcp.class.getClassLoader().getResourceAsStream("dbcp.properties");
Properties prop = new Properties();
prop.load(is);//将配置⽂件⾥的内容封装到prop对象内
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("pwd");
maxTotal= Integer.parseInt(prop.getProperty("maxTotal"));
maxIdle= Integer.parseInt(prop.getProperty("maxIdle"));
minIdle= Integer.parseInt(prop.getProperty("minIdle"));
initialSize= Integer.parseInt(prop.getProperty("initialSize"));
maxWaitMillis= Long.parseLong(prop.getProperty("maxWaitMillis"));
pool.setDriverClassName(driver);
pool.setUrl(url);
pool.setUsername(username);
pool.setPassword(password);
//连接池⽀持的最⼤连接数
//pool.setMaxTotal(maxTotal);
//连接池⽀持的最⼤空闲数
pool.setMaxIdle(maxIdle);
//⽀持的最⼩空闲数
pool.setMinIdle(minIdle);
//连接池对象创建时初始化的连接数
pool.setInitialSize(initialSize);
//空闲等待时间
//pool.setMaxWaitMillis(maxWaitMillis);
//注册驱动
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException
{
//从连接池中获取空闲对象
return pool.getConnection();
}
public static void closeConnection(Connection conn, Statement stat, ResultSet rs){
try {
if(rs!=null){
rs.close();
}
if(stat!=null){
stat.close();
}
if(conn !=null){
conn.close(); //会将连接对象归还给连接池内
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
c3p0
资源jar包
c3p0-0.9.5-pre8.jar
mchange-commons-java-0.2.7.jar
配置⽂件c3p0-config.xml
配置⽂件请放在src⽬录下
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">123456</property>
<property
name="jdbcUrl">jdbc:mysql://localhost:3306/mydb2</property>
<property
name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="acquireIncrement">10</property>
<property name="maxPoolSize">50</property>
<property name="minPoolSize">2</property>
<property name="initialPoolSize">5</property>
<property name="maxIdleTime">600</property>
</default-config>
</c3p0-config>
DBUtilc3p0类型的编写
package monrningwork02.util;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtilC3p0 {
//构造器会⾃动检索src下有没有指定⽂件名称的配置⽂件,然后会⾃动赋值给其相应的属性
private static ComboPooledDataSource pool = new
ComboPooledDataSource("c3p0-config");
public static Connection getConnection() throws SQLException
{
//从连接池中获取空闲对象
return pool.getConnection();
}
public static void closeConnection(Connection conn, Statement stat, ResultSet rs){
try {
if(rs!=null){
rs.close();
}
if(stat!=null){
stat.close();
}
if(conn !=null){
conn.close(); //会将连接对象归还给连接池内
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
druid
资源jar包
druid-1.1.18.jar
配置⽂件druid.properties
放在src⽬录下。注意,前⾯的key值是固定写法
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/qianfeng
username=root
password=123456
maxActive=20
minIdle=3
initialSize=5
maxWait=60000
DBUtildruid类型的编写
package monrningwork02.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DBUtildruid {
//创建连接池对象
private static DataSource pool = null;
static {
try {
//使⽤类加载器提供的⽅法读取db.properties,返回⼀个字节流对象
InputStream is = DBUtildruid.class.getClassLoader().getResourceAsStream("druid.properties");
//创建Properties对象,⽤于加载流内部的数据
Properties prop = new Properties();
prop.load(is); //加载流内部的信息,以key-value的形式进⾏加载
//调⽤静态⽅法,会⾃动给⾃⼰的属性赋值
pool = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
}
/**
* 获取连接对象
*
* @return 连接对象
* @throws SQLException
* @throws ClassNotFoundException
*/
public static Connection getConnection() throws SQLException, ClassNotFoundException {
//return DriverManager.getConnection(url, username,password);
//从连接池中获取连接对象
return pool.getConnection();
}
/**
* 关闭数据库连接
*
* @param rs 结果集对象
* @param stat 处理sql的执⾏对象Statement
* @param conn 连接对象
*/
public static void closeConnection(ResultSet rs, Statement stat, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (stat != null) {
stat.close();
}
if (conn != null) {
conn.close();//释放连接,归还给连接池
}
} catch (Exception e) {
System.out.println("数据库连接关闭失败");
e.printStackTrace();
}
}
}
DAO设计模式
DAO简介
- DAO是数据访问对象(Data Access Object)的简写。
- 建⽴在数据库与业务层之间,封装所有对数据库的访问操作,我们也可称之为持久层。
- ⽬的: 将数据访问逻辑和业务逻辑分开
⼀个DAO设计模式包含以下内容
- 定义实体类: 通过对象关系映射(ORM)将数据库的表结构映射成java类型;表中的每⼀条记录映射成类的实例。⽤于数据的传递。
- 定义⼀个接⼝: 在此接⼝中,定义应⽤程序对此表的所有访问操作,如增,删,改、查,等⽅法。
- 定义接⼝的实现类 实现接⼝中的所有抽象⽅法。
- 定义⼀个DAO⼯⼚类型 ⽤于返回接⼝实例 这样,开发⼈员只需要使⽤DAO接⼝即可,具体逻辑就变得透明了,⽆需了解内部细节。
dbutils第三⽅⼯具类的使⽤
简介
- 作⽤:
DBUtils是java编程中的数据库操作实⽤⼯具,⼩巧简单实⽤。
DBUtils封装了DAO层(持久层)的逻辑。减少了开发周期。
1.对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
2.对于数据表的写操作,也变得很简单(只需写sql语句)
3.可以使⽤数据源,使⽤JNDI,数据库连接池等技术来优化性能--重⽤已经构建好的数据库连接对象
- jar包:commons-dbutils-1.7.jar
- 常⽤API:
1. QueryRunner类型:可以直接使⽤连接池技术来操作数据库,进⾏增删改查
构造器:QueryRunner(DataSource ds)
返回⼀个指定数据库连接池得QueryRunner对象
⾮静态⽅法:query(String sql, ResultSetHandler<T> rsh)
通过sql,及其ReusltSetHandler的⼦类型来获取数据并封装成相应对象
它主要有三个⽅法
query() ⽤于执⾏select
update() ⽤于执⾏insert update delete
batch() 批处理
2. ResultSetHandler:关于结果集的⼀个接⼝。
⽤于定义select操作后,怎样封装结果集.
其实现类举例如下:
BeanHandler:将查询到的数据的第⼀条封装成实体类对象
BeanListHandler:将查询到的数据的第⼀条封装成实体类对象的集合
增删改代码测试
//第⼀种:使⽤的是⽆参的QueryRunner()⽅法----为了更加⽅便的使⽤
事务,因为可以直接获取到Connection对象
//1.创建⼲活的的对象--QueryRunner
QueryRunner qRunner = new QueryRunner();
//2.获取连接对象
Connection connection = C3P0Util.getConnection();
//3.调⽤update()⽅法实现对数据库的访问
int num = qRunner.update(connection, sql,6,"⻢六","345");
if (num >0) {
System.out.println("增加成功");
}else {
System.out.println("增加失败");
}
//第⼆种:使⽤的是有参的QueryRunner()⽅法--参数是数据源
//1.创建⼲活⼉的对象并绑定数据源
// QueryRunner qRunner2 = new
QueryRunner(C3P0Util.getDataSource());
// //2.调⽤update()⽅法
// int num2 = qRunner2.update(sql,7,"⻢六1","3451");
// if (num2 >0) {
// System.out.println("增加成功");
// }else {
// System.out.println("增加失败");
// }
}
查找功能实现
直接使⽤ResultSetHandler接⼝
public static User findUser1(User user) throws SQLException, ClassNotFoundException {
QueryRunner queryRunner = new QueryRunner();
Connection connection = DBUtildruid.getConnection();
return queryRunner.query(connection,"select * from user where password=? and name=?",new BeanHandler<User>(User.class),
user.getPassword(),user.getName());
}
我们发现通过实现ResultSetHandler接⼝,可以以各种形式获取数据库的数据,所以系统就封装了⼀批ResultSetHandler的⼦类实现各种功能.
ResultSetHandler下的所有结果处理器(⼦类)
注意:以下的⼦类中,重点掌握BeanHandler和BeanListHandler的功能实现
ArrayHandler:适合取1条记录。把该条记录的每列值封装到⼀个数组中Object[]
ArrayListHandler:适合取多条记录。把每条记录的每列值封装到⼀个数组中Object[],把数组封装到⼀个List中
ColumnListHandler:取某⼀列的数据。封装到List中。
KeyedHandler:取多条记录,每⼀条记录封装到⼀个Map中,再把这个Map封装到另外⼀个Map中,key为指定的字段值。
MapHandler:适合取1条记录。把当前记录的列名和列值放到⼀个Map中
MapListHandler:适合取多条记录。把每条记录封装到⼀个Map中,再把Map封装到List中
ScalarHandler:适合取单⾏单列数据
BeanHandler:返回⼀条记录-得到的是模型----重点掌握
BeanListHandler:返回所有的数据–将记录装⼊模型,将模型装⼊集合(list)—重点掌握
xml与json
XML
简介
eXtensible Markup Language 可扩展标记语⾔,⾥⾯的标记都是⽤户⾃定义的,标记都是成对的。作⽤是⽤来存储数据和传输数据。特别适合⽹络传输。
语法
* ⽂档声明:
* 必须写在xml⽂档的第⼀⾏。
* 写法:<?xml version="1.0" ?>
* 属性:
* version:版本号 固定值 1.0
* encoding:指定⽂档的码表。默认值为 iso-8859-1
* standalone:指定⽂档是否独⽴ yes 或 no
* 元素:xml⽂档中的标签
** ⽂档中必须有且只能有⼀个根元素
* 元素需要正确闭合。<body></body> <br/>
* 元素需要正确嵌套
* 元素名称要遵守:
* 元素名称区分⼤⼩写
* 数字不能开头
* ⽂本:
- 特殊符号:
< : <
> : >
& : &
" : "
' : '
* CDATA: ⾥边的数据会原样显示
* <![CDATA[ 数据内容 ]]>
* 属性:
* 属性值必须⽤引号引起来。单双引号都⾏
* 注释:
<!-- 注释内容 -->
* 处理指令:现在基本不⽤
<?xml-stylesheet type="text/css" href="1.css"?>
简单案例
<?xml version="1.0" encoding="UTF-8"?>
<书架>
<书 出版社=呵呵>
<书名>⾦瓶梅</书名>
<作者>陈冠希</作者>
<单价>10</单价>
<批发价>20</批发价>
</书>
<书>
<书名>葵花宝典</书名>
<作者>东⽅不败</作者>
<单价>10</单价>
</书>
</书架>
功能
数据存储
与⽂件对⽐
配置⽂件
作为配置⽂件存在,xml中主要配置的⼀些具有复杂的层级关系的数据
Properties⽂件中主要配置的⼀些key和value这样的数据。
数据传输
⼤部分使⽤json
json格式:{user:[{},{}],address:千锋}
{}代表map []代表数组
数据显示
可以使⽤html和xml,html主要⽤于⽹⻚显示.
与html的区别
xml是可扩展的标记语⾔,html是超⽂本标记语⾔
xml⾥的标记都是⽤户⾃定义的,html都是预定义的
xml⽤于存储和传输,html⽤于显示数据
html语法松散,xml语法严格
约束
1.约束就是xml的书写规则
2.约束的分类
dtd(Document Type Definition)约束
schema约束
3.dtd分类(简单约束)
内部dtd:在xml内部定义dtd
外部dtd:在外部⽂件中定义dtd
本地dtd⽂件:
本地dtd⽂件的编写规则介绍:以student.dtd为例
1.<!ELEMENT students (student*) > 构成:<!ELEMENT 元素名(规则(正则表达式))>,students是根节点,括号中是⽗节点的直接⼦节点
2.举例:正则表达式中 (student*)表示根节点的直接⼦节点必须都是student,并且可以出现0次或多次(name,age,sex) 表示student的元素是他们(#PCDATA)表示当前节点是叶⼦节点
3.<!ATTLIST student number ID #REQUIRED> !ATTLIST表示属性设置,number ID:表示每个student节点的ID号,必须是唯⼀的 #REQUIRED:表示ID号必须有
4.对应的xml中的设置:<!DOCTYPE students SYSTEM"student.dtd">,将这个设置放在⽂档声明的下⾯,表示当前⽂档遵守dtd的约束; 每个student节点的属性中都要标明 ID号,如:number="s0001",且每个ID号都不⼀样.
⽹络dtd⽂件:
原始⽂件 (student.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
<student number="s0001">
<name>zs</name>
<age>abc</age>
<sex>yao</sex>
</student>
</students>
dtd约束⽂件(student.dtd)
<!ELEMENT students (student*) >
<!ELEMENT student (name,age,sex)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ATTLIST student number ID #REQUIRED>
4.schema约束(详细约束)
导⼊xsd约束⽂档:
1、编写根标签
2、引⼊实例名称空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3、引⼊名称空间 xsi:schemaLocation="http://www.itcast.cn/xml student.xsd"
4、引⼊默认的名称空间
例⼦:
xmlns:xmlnamespace:xml的命名空间,命名空间标识是命名空间最重要的属性,重要到当输出⼀个命名空间时就直接转换为它的标识。标识有个规范的称呼:URI(统⼀资源定位符)。URI的最⼤特点是唯⼀性。如果不唯⼀就失去了辨识的意义。
xmlns="http://www.qianfeng.cn/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.qianfeng.cn/xml student.xsd"
json字符串
就是⽤特殊的字符串
需要使⽤双引号。
传输数据时多以json串形式传递数据,可读性差,但是效率⽐xml⾼
表示对象:必须使⽤{}
var json = "
{'city':'hangzhou','BESTJINGDIAN':'XIHU','SECONDJINGDIAN':'LEIFE NGTA'}"
表示数组:必须使⽤[]
var json = '[{"city":"hangzhou"},{"city":"beijing"},{"city":"shanghai"}]'
核⼼⾯试题
1.说出数据连接池的⼯作机制是什么?
J2EE服务器启动时会建⽴⼀定数量的池连接,并⼀直维持不少于此数⽬的池连接。客户端程序需要连接时,池驱动程序会返回⼀个未使⽤的池连接并将其标记为忙。如果当前没有空闲连接,池驱动程序就新建⼀定数量的连接,新建连接的数量由配置参数决定。当使⽤的池连接调⽤完成后,池驱动程序将此连接表记为空闲,其他调⽤就可以使⽤这个连接。
⼀个命名空间时就直接转换为它的标识。标识有个规范的称呼:URI(统⼀资源定位符)。URI的最⼤特点是唯⼀性。如果不唯⼀就失去了辨识的意义。
xmlns=“http://www.qianfeng.cn/xml”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://www.qianfeng.cn/xml student.xsd”
## json字符串
```json
就是⽤特殊的字符串
需要使⽤双引号。
传输数据时多以json串形式传递数据,可读性差,但是效率⽐xml⾼
表示对象:必须使⽤{}
var json = "
{'city':'hangzhou','BESTJINGDIAN':'XIHU','SECONDJINGDIAN':'LEIFE NGTA'}"
表示数组:必须使⽤[]
var json = '[{"city":"hangzhou"},{"city":"beijing"},{"city":"shanghai"}]'