存储过程 务的概念 事务的特性 关于异常的处理 连接池 构JdbcUtil类
1 存储过程
1)用当地数据库语言,写的一段业务逻辑算法,并该算法存储在客户端
2)使用存储过程需要用于CallableStatement接口,同时需要使如下SQL命令调用:{call add_pro(?,?,?)}
3)对于存储过程的输出参数,需要注册:
cstmt.registerOutParameter(3,Types.INTEGER);
4)取得返回值时,需要按照输出参数的位置来取
编写存储过程得到CallableStatement,并调用存储过程:
CallableStatement cstmt= conn.prepareCall("{call demoSp(?, ?)}");
设置参数,注册返回值,得到输出
cstmt.registerOutParameter(2, Types.VARCHAR);
cstmt.setString(1, "abcdefg");
cstmt.execute();
System.out.println(cStmt.getString(2));
- package cn.itcast.web.jdbc.dao;
- import java.sql.CallableStatement;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.Types;
- import cn.itcast.web.jdbc.util.JdbcUtil;
- //演示JDBC操作MySQL存储过程
- public class Demo1 {
- public static void main(String[] args) {
- Connection conn = null;
- //调用存储过程专用的接口
- CallableStatement cstmt = null;
- ResultSet rs = null;
- //存储过程特定的语法
- String sql = "{call add_pro(?,?,?)}";
- try {
- conn = JdbcUtil.getMySqlConnection();
- cstmt = conn.prepareCall(sql);
- //绑三个参数(前二个是输入,后一个是输出)
- cstmt.setInt(1,100);
- cstmt.setInt(2,200);
- //注册一个输出参数,
- //其中Types.INTEGER表示SQL与JDBC之前的是映射类型
- cstmt.registerOutParameter(3,Types.INTEGER);
- //调用存储过程
- boolean flag = cstmt.execute();
- System.out.println("flag="+flag);
- //取得执行结果
- int sum = cstmt.getInt(3);
- System.out.println("sum="+sum);
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- JdbcUtil.close(rs);
- JdbcUtil.close(cstmt);
- JdbcUtil.close(conn);
- }
- }
- }
2 事务的概念
1)每种数据库都有事务的支持,但支持强度不同
2)以MySQL为例,
启动事务
start transaction;
提交事务
commit;
回滚事务
rollback;
3)在事务范围内回滚是允许的,但如果commit后再回滚,无效
4)其实每条SQL都有事务存在,只是显示还隐藏而言,默认都是隐藏事务
5)事务的操作,必须争对同一个Connection。
6)事务的操作,可以设置一个回滚点,便于回滚到最近的回滚点处。
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
JDBC控制事务语句
•Connection.setAutoCommit(false);
•Connection.rollback();
•Connection.commit();
设置事务回滚点
•Savepoint sp = conn.setSavepoint();
•Conn.rollback(sp);
•Conn.commit(); //回滚后必须要提交
3 事务的特性
1)原子性(A)事务是的各个操作是一个不可分割的子操作。必须将其看成一个整体,即原子操作
2)一致性(C)事务前后,由一个一致状态转移到另一个一致状态
*3)隔离性(I)事务中,每个线程操作同张表同记录时,相互分割
4)持久性(D)事务一旦生效,在没有操作该记录时情况下,永远保持不变
*4 三个缺点(违背隔离性)
1)脏读:一个线程看到了另一个线程未提交的数据,叫脏读
2)不可重复读:一个线程多次做查询操作,多次结果都不一致,叫不可重复读
上述二项,强调的是查询,内容变,但数量不变
3)幻读/虚读:
上述一项,强调的是插入,数量变
*5 事务的隔离级别(解药)
*static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
*static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
不可重复读
read uncommitted |
read committed |
repeatable read |
serializable |
|
脏读 |
解决 |
解决 |
解决 |
|
不可重复读 |
解决 |
解决 |
||
幻读/虚读 |
解决 |
总结:
项目中,对于select操作不需要事务,对于其它操作(update/delete/insert)操作需要事务。
- package cn.itcast.web.jdbc.dao;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.Savepoint;
- import cn.itcast.web.jdbc.util.JdbcUtil;
- //JDBC显示操作事务的API
- public class Demo2 {
- public static void main(String[] args) {
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- String sqlA = "update account set salary=salary-1000 where name='aaa'";
- String sqlB = "update account set salary=salary+1000 where name='bbb'";
- String sqlC = "insert into account(name,salary) values('ccc',3000)";
- Savepoint sp = null;
- try {
- conn = JdbcUtil.getMySqlConnection();
- //设置事务显示手工提交
- conn.setAutoCommit(false);
- //NO1
- pstmt = conn.prepareStatement(sqlA);
- pstmt.executeUpdate();
- //NO2
- pstmt = conn.prepareStatement(sqlB);
- pstmt.executeUpdate();
- //设置一个回滚点
- sp = conn.setSavepoint();
- Integer.parseInt("abc");
- //NO3
- pstmt = conn.prepareStatement(sqlC);
- pstmt.executeUpdate();
- //设置事务手工提交
- conn.commit();
- } catch (Exception e) {
- e.printStackTrace();
- try {
- //事务回滚,默认情况下,回滚到事务开始之前的状态
- conn.rollback(sp);
- conn.commit();
- } catch (Exception e1) {
- }
- }finally{
- JdbcUtil.close(rs);
- JdbcUtil.close(pstmt);
- JdbcUtil.close(conn);
- }
- }
- }
- package cn.itcast.web.jdbc.dao;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import cn.itcast.web.jdbc.util.JdbcUtil;
- //JDBC设置事务的隔离级别
- public class Demo3 {
- //我(serializable)先执行
- public static void main(String[] args) {
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- String sql = "select * from account";
- try {
- conn = JdbcUtil.getMySqlConnection();
- //设置事务的隔离级别
- conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
- conn.setAutoCommit(false);
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- //休息
- Thread.sleep(20*1000);
- conn.commit();
- } catch (Exception e) {
- e.printStackTrace();
- try {
- conn.rollback();
- conn.commit();
- } catch (Exception e1) {
- }
- }finally{
- JdbcUtil.close(rs);
- JdbcUtil.close(pstmt);
- JdbcUtil.close(conn);
- }
- }
- }
*6 转帐案例
1)参见图<<转帐各类的交互图示>>
2)项目中,事务可能在dao层,也可能在service层,不论在哪一层,都必须确保使用的都是同一个connection
3)为了确保在Service和Dao层中用到的Connection一致,你可以使用如下方案解决:
a)将Service中的Connection传入Dao中
设计缺点:
Service和Dao代码过分藕合
在Service中引用了非业务逻辑操作
b)将JdbcUtil类中的Connection作成单例/态
c)使用ThreadLocale<Connection>将每个线程和自已的Connection绑定在一起,每个线程修改自已的Connection,
不会影响其它线程的Connection
4)在分层结构中,关闭Connection会推迟到Service层,但一定要关闭Connection对象
- <%@ page language="java" pageEncoding="UTF-8"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <body>
- <form action="/day14/TransferServlet" method="post">
- <table border="1" align="center">
- <caption>转帐</caption>
- <tr>
- <th>转出帐号</th>
- <td><input type="text" name="sid"/></td>
- </tr>
- <tr>
- <th>转入帐号</th>
- <td><input type="text" name="tid"/></td>
- </tr>
- <tr>
- <th>金额</th>
- <td><input type="text" name="money"/></td>
- </tr>
- <tr>
- <td colspan="2" align="center">
- <input type="submit" value="转帐"/>
- </td>
- </tr>
- </table>
- </form>
- </body>
- </html>
- package cn.itcast.web.jdbc.domain;
- //帐户
- public class Account {
- private int id;//帐号
- private String name;//用户名
- private float salary;//薪水
- public Account(){}
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public float getSalary() {
- return salary;
- }
- public void setSalary(float salary) {
- this.salary = salary;
- }
- }
- package cn.itcast.web.jdbc.dao;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import cn.itcast.web.jdbc.domain.Account;
- import cn.itcast.web.jdbc.util.JdbcUtil;
- public class TransferDao {
- //根据ID号查询帐户
- public Account findAccountById(int id) throws SQLException{
- Account account = null;
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- String sql = "select * from account where id = ?";
- try {
- conn = JdbcUtil.getMySqlConnection();
- pstmt = conn.prepareStatement(sql);
- pstmt.setInt(1,id);
- rs = pstmt.executeQuery();
- if(rs.next()){
- account = new Account();
- account.setId(id);
- account.setName(rs.getString("name"));
- account.setSalary(rs.getFloat("salary"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- JdbcUtil.close(rs);
- JdbcUtil.close(pstmt);
- //JdbcUtil.close(conn);
- }
- return account;
- }
- //根据ID号更新帐户
- public void updateAccountById(Account newAccount) throws SQLException{
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- String sql = "update account set salary = ? where id = ?";
- try {
- conn = JdbcUtil.getMySqlConnection();//conn=123
- pstmt = conn.prepareStatement(sql);
- pstmt.setFloat(1,newAccount.getSalary());
- pstmt.setInt(2,newAccount.getId());
- pstmt.executeUpdate();
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- JdbcUtil.close(rs);
- JdbcUtil.close(pstmt);
- //JdbcUtil.close(conn);
- }
- }
- }
- package cn.itcast.web.jdbc.service;
- import cn.itcast.web.jdbc.dao.TransferDao;
- import cn.itcast.web.jdbc.domain.Account;
- import cn.itcast.web.jdbc.exception.NoAccountException;
- import cn.itcast.web.jdbc.exception.NoMoneyException;
- import cn.itcast.web.jdbc.util.JdbcUtil;
- public class TransferService {
- //转帐
- public void transfer(int sid,int tid,float money) throws Exception{
- //NO1:判段转入和转出帐号是否存在
- TransferDao transferDao = new TransferDao();
- Account sAccount = transferDao.findAccountById(sid);
- Account tAccount = transferDao.findAccountById(tid);
- if(sAccount!=null && tAccount!=null){
- //NO2:判段转出帐号是否有足够的余额
- if(sAccount.getSalary()-money >= 0){
- //进行转帐操作
- sAccount.setSalary(sAccount.getSalary() - money);
- tAccount.setSalary(tAccount.getSalary() + money);
- try {
- //事务开始
- JdbcUtil.begin();//conn=123
- transferDao.updateAccountById(sAccount);
- //int i = 10/0;
- transferDao.updateAccountById(tAccount);
- //事务提交
- JdbcUtil.commit();
- } catch (Exception e) {
- e.printStackTrace();
- try {
- //事务回滚
- JdbcUtil.rollback();
- //事务提交
- JdbcUtil.commit();
- } catch (Exception e1) {
- }
- throw e;
- }finally{
- //关闭Connection对象
- JdbcUtil.closeConnection();
- }
- }
- }
- }
- //取款
- public void withdraw(int sid, float money)throws Exception{
- TransferDao transferDao = new TransferDao();
- Account sAccount = transferDao.findAccountById(sid);
- if(sAccount!=null){
- if(sAccount.getSalary()-money >= 0){
- sAccount.setSalary(sAccount.getSalary() - money);
- try {
- transferDao.updateAccountById(sAccount);
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- JdbcUtil.closeConnection();
- }
- }else{
- throw new NoMoneyException();
- }
- }else{
- throw new NoAccountException();
- }
- }
- }
- package cn.itcast.web.jdbc.web;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import cn.itcast.web.jdbc.exception.NoAccountException;
- import cn.itcast.web.jdbc.exception.NoMoneyException;
- import cn.itcast.web.jdbc.service.TransferService;
- public class TransferServlet extends HttpServlet {
- private void withdraw(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
- try {
- int sid = Integer.parseInt(request.getParameter("sid"));
- float money = Float.parseFloat(request.getParameter("money"));
- TransferService transferService = new TransferService();
- transferService.withdraw(sid,money);
- request.setAttribute("message","交易成功,请取款");
- }catch(NoMoneyException e){
- e.printStackTrace();
- request.setAttribute("message","帐号余额不足,不能交易");
- }catch(NoAccountException e){
- e.printStackTrace();
- request.setAttribute("message","帐号输入错误,请重试");
- } catch (Exception e) {
- e.printStackTrace();
- request.setAttribute("message","交易失败,请重试");
- }
- request.getRequestDispatcher("/message.jsp").forward(request,response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
- String method = request.getParameter("method");
- if(method!=null && method.equals("withdraw")){
- this.withdraw(request,response);
- return;
- }
- try {
- int sid = Integer.parseInt(request.getParameter("sid"));
- int tid = Integer.parseInt(request.getParameter("tid"));
- float money = Float.parseFloat(request.getParameter("money"));
- TransferService transferService = new TransferService();
- transferService.transfer(sid,tid,money);
- //转帐成功
- request.setAttribute("message","转帐成功");
- } catch (Exception e) {
- //转帐失败
- request.setAttribute("message","转帐成功");
- }
- request.getRequestDispatcher("/message.jsp").forward(request,response);
- }
- }
7 关于异常的处理
1)关于分层结构中,处理异常的规则,参见<<关于异常的处理规则.JPG>>
2)异常在项目中,往往替代boolean值,作为成功与否的标志
*8 连接池
1)传统方式找DriverManager要连接,数目是有限的。
2)传统方式的close(),并没有将Connection重用,只是切断应用程序和数据库的桥梁,即无发送到SQL命令到数据库端执行
3)项目中,对于Connection不说,不会直接使用DriverManager取得,而使用连接池方式。
4)DBCP和C3P0,都是Java开源的,都必须直接或间接实现javax.sql.DataSource接口
5)DBCP连接池需要dbcp.properties文件,同时需加入3个对应的jar包
•commons-dbcp.jar:连接池的实现
•commons-pool.jar:连接池实现的依赖类
•commons-collections.jar :连接池实现的集合类
*6)C3P0连接池需要在/WEB-INF/classes/目录下存放c3p0-config.xml文件,该类ComboPooledDataSource在创建时
会自动在指定的目录下找xml文件,并加载默认设置
7)重构JdbcUtil类
- package cn.itcast.web.jdbc.datasource;
- import java.sql.Connection;
- import java.sql.SQLException;
- import cn.itcast.web.jdbc.util.JdbcUtil3;
- //测试传统方式取得连接的时间和个数
- public class Demo1 {
- public static void main(String[] args) throws SQLException {
- long begin = System.currentTimeMillis();
- for(int i=1;i<=5000;i++){
- Connection conn = JdbcUtil3.getMySqlConnection();
- if(conn!=null){
- System.out.println(i+":取得连接");
- }
- JdbcUtil3.close(conn);
- }
- long end = System.currentTimeMillis();
- System.out.println("共用" + (end-begin)/1000+"秒");
- }
- }
- package cn.itcast.web.jdbc.datasource;
- import java.io.InputStream;
- import java.sql.Connection;
- import java.util.Properties;
- import javax.sql.DataSource;
- import org.apache.commons.dbcp.BasicDataSourceFactory;
- //测试连接池DBCP的用法
- public class Demo2 {
- public static void main(String[] args) throws Exception {
- long begin = System.currentTimeMillis();
- //加载属性文件
- InputStream is = Demo2.class.getClassLoader().getResourceAsStream("cn/itcast/web/jdbc/config/dbcp.properties");
- Properties props = new Properties();
- props.load(is);
- //创建DBCP连接池工厂
- BasicDataSourceFactory factory = new BasicDataSourceFactory();
- //创建数据源,即连接池
- DataSource ds = factory.createDataSource(props);
- for(int i=1;i<=50000;i++){
- //从连接池中取得一个空闲的连接对象
- Connection conn = ds.getConnection();
- if(conn!=null){
- System.out.println(i+":取得连接");
- }
- //将连接对象还回给连接池
- conn.close();
- }
- long end = System.currentTimeMillis();
- System.out.println("共用" + (end-begin)/1000+"秒");
- }
- }
- driverClassName=com.mysql.jdbc.Driver
- url=jdbc:mysql://127.0.0.1:3306/mydb2
- username=root
- password=root
- package cn.itcast.web.jdbc.datasource;
- import java.sql.Connection;
- import com.mchange.v2.c3p0.ComboPooledDataSource;
- //测试连接池C3P0的用法
- public class Demo3 {
- public static void main(String[] args) throws Exception {
- long begin = System.currentTimeMillis();
- //创建C3P0连接池
- ComboPooledDataSource dataSource = new ComboPooledDataSource();
- for(int i=1;i<=100000;i++){
- Connection conn = dataSource.getConnection();
- if(conn!=null){
- System.out.println(i+":取得连接");
- conn.close();
- }
- }
- long end = System.currentTimeMillis();
- System.out.println("共用" + (end-begin)/1000+"秒");
- }
- }
存储过程 务的概念 事务的特性 关于异常的处理 连接池 构JdbcUtil类的更多相关文章
- 阶段3 1.Mybatis_07.Mybatis的连接池及事务_4 mybatis中使用unpooled配置连接池的原理分析
把之前的CRUD的代码src下的代码都复制过来 依赖项也都复制过来, 配置文件 整理一番 执行findAll方法的测试 查看日志的输出部分 修改程序池 再来执行findAll方法 Plooled从连接 ...
- 连接SQLServer时,因启用连接池导致孤立事务的原因分析和解决办法
本文出处:http://www.cnblogs.com/wy123/p/6110349.html 之前遇到过这么一种情况: 连接数据库的部分Session会出现不定时的阻塞,这种阻塞时长时短,有时候持 ...
- Mybatis连接池及事务
一:Mybatis连接池 我们在学习WEB技术的时候肯定接触过许多连接池,比如C3P0.dbcp.druid,但是我们今天说的mybatis中也有连接池技术,可是它采用的是自己内部实现了一个连接池技术 ...
- 【概念原理】四种SQL事务隔离级别和事务ACID特性
摘要: SQL事务隔离级别和事务的ACID特性 事务是一组读写操作,并且具有只有所有操作都成功才算成功的特性. 事务隔离级别 SQL事务隔离级别由弱到强分别是:READ_UNCOMMITTED.R ...
- 数据库MySQL之 视图、触发器、存储过程、函数、事务、数据库锁、数据库备份、事件
数据库MySQL之 视图.触发器.存储过程.函数.事务.数据库锁.数据库备份.事件 浏览目录 视图 触发器 存储过程 函数 事务 数据库锁 数据库备份 事件 一.视图 1.视图概念 视图是一个虚拟表, ...
- Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题
Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...
- MySQL数据库事务及其特性
一.事务概念 事务就是一个程序执行单元,里面的操作要么都做,要么都不做. 二.事务特性 事务有四个非常重要的特性(ACID): 原子性(Atomicity):事务是不可分割的整体,所有操作要么全做,要 ...
- 事务、事务特性、事务隔离级别、spring事务传播特性
事务.事务特性.事务隔离级别.spring事务传播特性 1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功, ...
- 什么是事务、事务特性、事务隔离级别、spring事务传播特性
1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败). 2.事务特性: 事务特性分为四个:原子性(At ...
随机推荐
- L010-oldboy-mysql-dba-lesson10
L010-oldboy-mysql-dba-lesson10 来自为知笔记(Wiz)
- Angularjs在线编辑器
1.TextAngular: https://github.com/fraywing/textAngular textAngular是一个强大的Text-Editor/Wysiwyg 编辑器,用于An ...
- ng-src作用
... <ul class="phones"> <li ng-repeat="phone in $ctrl.phones | filter:$ctrl. ...
- javascript实现URL不缓存的方法
<script> document.write("<s"+"cript type='text/javascript' src='/js/test.js? ...
- ComboBox Control Messages 消息
连接到MSDN,有时间完善这个.具体说明可点击进入msdn CB_ADDSTRING 添加一个字符串组合框的列表框.如果组合框没有cbs_sort风格,字符串添加到列表的结尾.否则,该字符串插入列表, ...
- Javacript 客户端保存数据[ locaStorage ]
1.通常程序员们会使用Cookie进行一些小量的数据储存在客户端浏览器,但孰不知这样会造成不必要的带宽浪费 ,可使用 js 中的 locaStorage 来替代cookie进行存储,但不支持IE8以下 ...
- ios播放声音中断后台音乐的问题
今天遇到一个ios播放声音中断后台音乐的问题,在我的app中如果调用AVAudioSession 播放完声音,后台的qq音乐偶尔不能恢复,而网易云音乐一次都不能恢复播放,研究了一下AVAudioS ...
- SVN 提交必填备注Commit
操作方法:在SVN的Repositories下,找到要配置的项目,在项目目录下找到hooks文件夹,在其下创建pre-commit.bat文件,把下面复制进去就可以了(无需重启,如果改动,保存bat文 ...
- Windows 7下载
原版的ISO:windows 7 旗舰版:32位: ed2k://|file|cn_windows_7_ultimate_x86_dvd_x15-65907.iso|2604238848|D6F139 ...
- 一个 XSD 实例
一个 XSD 实例 本节会为您演示如何编写一个 XML Schema.您还将学习到编写 schema 的不同方法. XML 文档 让我们看看这个名为 "shiporder.xml" ...