java事务 存储过程 事务_Java数据库连接--JDBC调用存储过程,事务管理和高级应用...

一、JDBC常用的API深入详解及存储过程的调用

1、存储过程的介绍

我们常用的操作数据库语言SQL语句在执行的时候要先进行编译,然后执行,而存储过程是在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过制定存储过程的名字并给出参数(如果该存储过程带有参数) 来执行它。存储过程是数据库中 的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。

一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或者平台上执行相同函数,或者封装特定功能时,存储过程非常有用。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。

存储过程有以下优点:

(1).存储过程增强了SQL语言的功能和灵活性。存储过程可以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。

(2).存储过程允许标准组件是编程。存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库

专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。

(3).存储过程能实现较快的执行速度。如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。

因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL

语句在每次运行时都要进行编译和优化,速度相对要慢一些。

(4).存储过程能过减少网络流量。针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织程存储过程,

那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大增加了网络流量并降低了网络负载。

(5).存储过程可被作为一种安全机制来充分利用。系统管理员通过执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免

了非授权用户对数据的访问,保证了数据的安全。

简单说,好处主要:

1、由于数据执行动作时,先编译后执行,然而存储过程是一个编译过的代码块,所以执行效率比T-SQL高。

2、一个存储过程在程序中交互可以替代大队的T-SQL语句,所以也能降低网络的通信量,提高通信效率。

3、通过存储过程能够使没有权限的用户在控制之下间接存取数据库,保证数据安全。

2、Jdbc调用无参数存储过程

存储过程代码:

CREATE PROCEDURE imooc_db.sp_select_nofilter()

BEGIN

select*from imooc_goddess;

END;

调用代码:

Connection conn=DBUtil.getConnection();

CallableStatement c=conn.prepareCall("call sp_select_nofilter()");

c.execute();

ResultSet rs=c.getResultSet();

在数据库中新建存储过程:

72fe3d114046150ceb3226821e1951b5.png

注意:创建的存储过程名称不要加"()",不然在调用存储过程会出错。

代码示例:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packageproduceDao;2

3 importjava.sql.CallableStatement;4 importjava.sql.Connection;5 importjava.sql.ResultSet;6 importjava.sql.SQLException;7

8 importsql.Dao.DBUtil;9

10 public classProduceDAO {11 public void select_nofilter() throwsSQLException{12 //1、获得连接

13 Connection connection=DBUtil.getConnection();14 //2、获得CallableStatement

15 CallableStatement cs=connection.prepareCall("call sp_select_nofilter()");16 //3、执行存储过程

17 cs.execute();18 //4、处理返回结果:结果集,出参

19 ResultSet rs=cs.getResultSet();20 //遍历结果集

21 while(rs.next()){22 System.out.println(rs.getString("user_name")+":"+rs.getString("email"));23 }24 }25 }

ProduceDao.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packageproducetest;2

3 importjava.sql.SQLException;4 importproduceDao.ProduceDAO;5

6 public classJdbcProduceTest {7 public static void main(String []args) throwsSQLException{8 ProduceDAO dao=newProduceDAO();9 dao.select_nofilter();10 }11 }

JdbcProduceTest.java

运行结果:

0ce48ac973cfa36ce0bb8b97d5090546.png

3、Jdbc调用含输入参数存储过程

新建一个过程:

8f4badc71af744303e02b921db9ad34d.png

如果输入空白字符,会全部显示,结果如下:

26a1b83fee275ae6e03d8b06b257a239.png

如果传入一个'蔡',则结果如下:

5f1ca249292413c65c01d18f8aa9f5f8.png

1 //只有输入in,没有输出的存储过程

2 public List select_filter(String sp_name) throwsSQLException{3 List result=new ArrayList<>();4 //1、获得连接

5 Connection connection=DBUtil.getConnection();6 //2、获得CallableStatement

7 CallableStatement cs=connection.prepareCall("call sp_select_filter(?)");8 cs.setString(1, sp_name);9 //3、执行存储过程

10 cs.execute();11 //4、处理返回结果:结果集,出参

12 ResultSet rs=cs.getResultSet();13 Goddess goddess=null;14 //遍历结果集

15 while(rs.next()){16 goddess=newGoddess();17 goddess.setId(rs.getInt("id"));18 goddess.setUserName(rs.getString("user_name"));19 goddess.setAge(rs.getInt("age"));20 result.add(goddess);21 }22 returnresult;23 }

测试:

1 public classJdbcProduceTest {2 public static void main(String []args) throwsSQLException{3 ProduceDAO dao=newProduceDAO();4 //dao.select_nofilter();

5 String sp_name="白";6 List res=null;7 res=dao.select_filter(sp_name);8 for(int i=0;i

运行结果:

8b2d2d2b2140df2834609debca2b5353.png

4、Jdbc调用含输出参数存储过程

建立一个过程

dfe689c0421bba673785e98ef3a0000c.png

调用存储过程:显示出一共多少记录,结果如下:

87c065349c155669065ed71f86165125.png

含有输出参数的存储过程

1 //Jdbc调用含有输出参数的的存储过程

2 public Integer select_count()throwsSQLException{3 Integer count=0;4 Connection connection=DBUtil.getConnection();5 CallableStatement cs=connection.prepareCall("call sp_select_count(?)");6 cs.registerOutParameter(1, Types.INTEGER);//注册输出参数,第二个参数时告诉jdbc,输出参数的类型。

7 cs.execute();8 //处理返回的结果:这个结果不是结果集,而是出参

9 count=cs.getInt(1);10 returncount;11 }

测试:

1 public classJdbcProduceTest {2 public static void main(String []args) throwsSQLException{3 ProduceDAO dao=newProduceDAO();4 // //dao.select_nofilter();5 //String sp_name="蔡";6 //List res=null;ss7 //res=dao.select_filter(sp_name);8 //for(int i=0;i

11 String sp_name="蔡";12 List res=null;13 Integer count=0;14

15 //带输入参数的存储过程

16 res=select_filter(sp_name);17 showResult(res);18

19 count=select_count();20 System.out.println("一共有"+count+"个女神!");21 }22

23 public static List select_filter(String sp_name)throwsSQLException{24 ProduceDAO dao=newProduceDAO();25 returndao.select_filter(sp_name);26 }27 public static Integer select_count()throwsSQLException{28 ProduceDAO dao=newProduceDAO();29 returndao.select_count();30 }31 public static void showResult(Listresult){32 for(int i=0;i

37 }

运行结果:

59463af6dba24f81dc4233460bedda8e.png

二、JDBC的事务管理

事务的概念:

事务是作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体一起向系统提交,要么都执行,要么都不执行。

事务的特点:

1、原子性:事务是一个完整的操作。不能对它进行再分割,是最小的一个单元。

2、一致性:当事务完成时,数据必须处于一致状态。(例如银行转账,张三要给李四转100元。则第一步张三的账户需要减去100元,第二步李四的账户需要加上100元。这是两个操作,但是应该在一个事务里面。如果没有在一个事务里面,张三减去100,李四并没有增加100,那这样数据就出现了不一致性,张三的钱跑哪去了呢 )

3、隔离性:对数据进行修改的所有并发事务是彼此隔离的。(比如业务A:张三减100,李四加100;同时业务B也是张三减100,李四加100进行操作。业务A和B是同时的,这时候就出现了并发,这个时候是怎么变化的呢?当业务员A进行操作的时候,业务员B就要等待……就是同一时间对数据库的操作要保持一个事务的锁定。也就是说我在做的时候,别人是不能做的。我做完了之后别人才能做,彼此之间是隔离的)

4、永久性:事务完成之后,它对数据库的修改是永久保存的。

1、Jdbc实现事务管理

(1)、我们通过提交commit()或者回退rollback()来管理事务的操作。

当事务完成之后,我们通过commit将事务提交到数据库之中,然后数据库会变成持久化的,我们的数据就会永久保存了。

如果采用rollback的话,事务回滚,比如说我们插入的数据、更新的数据都会变成原来没有更新、没有插入的样子。

(2)、事务操作默认是自动提交的

当我们调用完insert语句,不用调用commit语句,自己就自动提交了。

(3)、可以调用setAutoCommit(false)来禁止自动提交。

2、通过代码实现事务的管理

首先我们要注意,在JDBC中,事务操作默认是自动提交。也就是说,一条对数据库的更新表达式代表一项事务操作。操作成功后,系统将自动调用commit()来提交,否则将调用rollback()来回退。

其次,在JDBC中,可以通过调用setAutoCommit(false)来禁止自动提交。之后就可以把多个数据库操作的表达式作为一个事务,在操作完成 后调用commit()来进行整体提交。倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常。此时就可以在异常捕获时调用 rollback()进行回退。这样做可以保持多次更新操作后,相关数据的一致性。

1 private static final String URL="jdbc:mysql://localhost:3306/mydb";2private static final String NAME="root";3 private static final String PASSWORD="mysql123";4 try{5 conn=DriverManager.getConnection(URL,NAME,PASSWORD);6 conn.setAutoCommit(false);//禁止自动提交,设置回退

7 stmt=conn.createStatement();8 //数据库更新操作1

9 stmt.executeUpdate("update firsttable Set Name='testTransaction' where ID=1");10 //数据库更新操作2

11 stmt.executeUpdate("insert into firsttable ID=12,Name='testTransaction2'");12 //事务提交

13 conn.commit();14 }catch(Exception e){15 e.printStackTrace();16 try{17 //操作不成功则回退

18 conn.rollback();19 } catch(Exception ex) {20 //TODO: handle exception

21 ex.printStackTrace();22 }23 }

三、数据库连接池(dbcp、c3p0)

连接池产生的背景:

数据库连接是一种重要资源。大部分很重要的数据都存在数据库里,那么在产生连接池之前,我们连接数据库的方式:直连。(获取连接->使用->关闭连接)程序小的话可以采用这种方式,但是如果程序很大,比如大型网站,它可能每分钟或者每秒变化量在100万次,就是说同时访问数据库有100万个用户,这时候如果我们不用连接池的话,我们就需要创建100万个连接这样的话就会对数据库造成很大的压力,如果数据库承受不了的话就崩溃了,服务器也崩溃了,网站就瘫痪了。

即:

①数据库连接是一种重要资源;

②频繁的连接数据库会增加数据库的压力;

③为解决以上问题出现连接池技术。

(池子里保持一定数量的连接,当使用时就从池子中拿一个连接出来,当使用完连接后就把它释放到池子里。当你同时访问数据库人很多的时候,这个时候连接不够用,就需要等待,减少数据库的压力)

常用的开源数据库连接池:

dbcp

c3p0

1、dbcp的使用步骤

1、首先,导入相关jar包:

commons-dbcp-1.4.jar

commons-pool-1.5.6.jar

commons-logging-1.1.1.jar

2、根目录下设置配置文件(/src下):dbcp.properties

1 driverClassName=com.mysql.jdbc.Driver2 url=jdbc:mysql://localhost:3306/mydb

3 username=root4 password=mysql1235

6 maxIdle=30

7 maxIdle=10

8 maxWait=1000

9

10 removeAbandoned=true

11 removeAbandonedTimeout=180

3、配置并测试dbcp连接

案例一:dbcptest.java

1 packagedb;2

3 importjava.sql.Connection;4 importjava.sql.SQLException;5

6 importorg.apache.commons.dbcp.BasicDataSource;7

8 public classtestjdbc {9 public static void main(String []args) throwsSQLException{10 BasicDataSource dataSource=null;11 dataSource=newBasicDataSource();12 dataSource.setDriverClassName("com.mysql.jdbc.Driver");13 dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/mydb");14 dataSource.setUsername("root");15 dataSource.setPassword("mysql123");16 Connection connection=dataSource.getConnection();17 System.out.println(connection);18 }19 }

运行结果:

1fd6d05e3d4cc7066d7188961289c39c.png

案例二:

DBCPUtil.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagedb;2

3 importjava.sql.Connection;4 importjava.sql.SQLException;5 importjava.util.Properties;6

7 importjavax.sql.DataSource;8

9 importorg.apache.commons.dbcp.BasicDataSource;10 importorg.apache.commons.dbcp.BasicDataSourceFactory;11

12 public classDBCPUtil {13 private staticDataSource DS;14 private static final String configFile="dbcp.properties";15

16 /**

17 * 从数据源获得一个连接18 *@throwsSQLException19 **/

20

21 public Connection getConn() throwsSQLException,Exception{22 Connection conn=null;23 if(DS!=null){24 conn=DS.getConnection();//从数据源里拿到连接

25 conn.setAutoCommit(false);//关闭连接的自动提交

26 returnconn;27 }28 returnconn;29 }30

31

32 /**

33 * 默认的构造函数34 *@throwsException35 * **/

36 public DBCPUtil() throwsException{37 initDbcp();38 }39

40 private static void initDbcp() throwsException{41 Properties pops=newProperties();42 pops.load(DBCPUtil.class.getClassLoader().getResourceAsStream(configFile));43

44 DS=BasicDataSourceFactory.createDataSource(pops);//通过BasicDataSourceFactory提供的工厂类,拿到DataSource数据源

45 }46

47 //构造函数,初始化了DS,指定数据库

48 publicDBCPUtil(String url){49 initDS(url);50 }51

52 //构造函数,初始化了DS,指定了所有参数

53 public DBCPUtil(String connectURL,String username,String password,String driverClass,intinitialSize,54 int maxIdle,int minIdle,intmaxWait){55 initDS(connectURL,username,password,driverClass,initialSize,maxIdle,minIdle,maxWait);56 }57

58 public static voidinitDS(String url){59 initDS(url,"root","mysql123","com.mysql.jdbc.Driver",10,20,5,1000);60 }61 public static void initDS(String connectURL,String userName,String password,String driverClass,int initialSize,int maxIdle,int minIdle,intmaxWait){62 BasicDataSource ds=new BasicDataSource();//new一个数据源

63 ds.setDriverClassName(driverClass);64 ds.setUsername(userName);65 ds.setPassword(password);66 ds.setUrl(connectURL);67 ds.setInitialSize(initialSize);68 ds.setMaxIdle(maxIdle);69 ds.setMaxWait(maxWait);70 ds.setMinIdle(minIdle);71 DS=ds;72 }73 }

DBCPUtil.java

GoddessDao.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 //查询单个女神

2 public Goddess get(Integer id) throwsSQLException{3 Goddess goddess=null;4 Connection connection=DBUtil.getConnection();5 String sql=""+

6 "select * from imooc_goddess "+

7 "where id=?";8 PreparedStatement psmt=connection.prepareStatement(sql);9 psmt.setInt(1, id);10 //psmt.execute();execute()是执行更改数据库操作(包括新增、修改、删除);executeQuery()是执行查询操作

11 ResultSet rsResultSet=psmt.executeQuery();12 while(rsResultSet.next()){13 goddess=newGoddess();14 goddess.setId(rsResultSet.getInt("id"));15 goddess.setUserName(rsResultSet.getString("user_name"));16 goddess.setSex(rsResultSet.getString("sex"));17 goddess.setAge(rsResultSet.getInt("age"));18 goddess.setBirthday(rsResultSet.getDate("birthday"));19 goddess.setEmail(rsResultSet.getString("email"));20 goddess.setMobile(rsResultSet.getString("mobile"));21 goddess.setCreateUser(rsResultSet.getString("create_user"));22 goddess.setCreateDate(rsResultSet.getDate("create_date"));23 goddess.setUpdateUser(rsResultSet.getString("update_user"));24 goddess.setUpdateDate(rsResultSet.getDate("update_date"));25 goddess.setIsDel(rsResultSet.getInt("isdel"));26 }27 returngoddess;28 }29 //查询单个女神(根据id去查询,DBCP连接池的方式)

30 public Goddess getByDbcp(Integer id) throwsException{31 //TODO Auto-generated method stub

32 DBCPUtil db=new DBCPUtil();//33 Goddess goddess=null;34 Connection connection=db.getConn();//拿到数据库的连接,通过DBCP

35 String sql=""+

36 "select * from imooc_goddess "+

37 "where id=?";38 PreparedStatement psmt=connection.prepareStatement(sql);39 psmt.setInt(1, id);40 //psmt.execute();execute()是执行更改数据库操作(包括新增、修改、删除);executeQuery()是执行查询操作

41 ResultSet rsResultSet=psmt.executeQuery();42 while(rsResultSet.next()){43 goddess=newGoddess();44 goddess.setId(rsResultSet.getInt("id"));45 goddess.setUserName(rsResultSet.getString("user_name"));46 goddess.setSex(rsResultSet.getString("sex"));47 goddess.setAge(rsResultSet.getInt("age"));48 goddess.setBirthday(rsResultSet.getDate("birthday"));49 goddess.setEmail(rsResultSet.getString("email"));50 goddess.setMobile(rsResultSet.getString("mobile"));51 goddess.setCreateUser(rsResultSet.getString("create_user"));52 goddess.setCreateDate(rsResultSet.getDate("create_date"));53 goddess.setUpdateUser(rsResultSet.getString("update_user"));54 goddess.setUpdateDate(rsResultSet.getDate("update_date"));55 goddess.setIsDel(rsResultSet.getInt("isdel"));56 }57 returngoddess;58 }

GoddessDao.java

测试:DbcpTest.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagedb;2

3 importjava.sql.SQLException;4

5 importjava.util.Date;6

7 importsql.Dao.GoddessDao;8 importsql.model.Goddess;9 public classDbcpTest {10 public static void main(String []args) throwsException{11 //1、通过普通的方式操作数据库

12 Date a=newDate();13 get();14 Date b=newDate();15 System.out.println(b.getTime()-a.getTime());16

17 //2、通过DBCP连接池的方式操作数据库

18 Date c=newDate();19 getByDbcp();20 Date d=newDate();21 System.out.println(d.getTime()-c.getTime());22

23 //通过运行发现,第一种方式要比第二种方式花费时间要多

24

25 }26

27 private static void get() throwsSQLException {28 //TODO Auto-generated method stub

29 GoddessDao dao=newGoddessDao();30 Goddess goddess=dao.get(2);31 System.out.println("普通连接:"+goddess.toString());32 }33 private static void getByDbcp() throwsException {34 //TODO Auto-generated method stub

35 GoddessDao dao=newGoddessDao();36 Goddess goddess=dao.getByDbcp(2);37 System.out.println("dbcp连接:"+goddess.toString());38 }39 }

DbcpTest.java

运行结果:

7e75d4779dca7bebd64dadff7544fad6.png

2、c3p0使用步骤

c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。

默认情况下(即没有配置连接池的情况下),Hibernate会采用内建的连接池。但这个连接池性能不佳,因此官方也只是建议仅在开发环境下使用。Hibernate支持第三方的连接池,官方推荐的连接池是C3P0,Proxool。

1、导入相关jar包

c3p0-0.9.2.jar

mchange-commons-java-0.2.3.4.jar

2、配置根目录配置文件

1 <?xml version="1.0" encoding="UTF-8"?>

2

3

4 com.mysql.jdbc.Driver

5 jdbc:mysql://127.0.0.1:3306/mydb

6 root

7 mysql123

8

9 50

10

11

12 5

13

14

15 50

16

17 1000

18

19

20 20

21

22 5

23

24

25

3、测试

实例一:C3P0Test.java

1 packagec3p0;2

3 importjavax.sql.DataSource;4

5 importcom.mchange.v2.c3p0.ComboPooledDataSource;6

7 public classC3poTest {8 private static final String helloC3p0="c3p0-config.xml";9 public static void main(String []args) throwsException{10 //ComboPooledDataSource cpds=new ComboPooledDataSource();11 //cpds.setDriverClass("com.mysql.jdbc.Driver");12 //cpds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mydb");13 //cpds.setUser("root");14 //cpds.setPassword("mysql123");

15 DataSource dataSource=new ComboPooledDataSource("helloC3p0");16 System.out.println(dataSource.getConnection());17 }18 }

运行结果:

47c84ddbd04df3d45f4a5889b1784022.png

3、连接池总结

f0daeb4ed32ac4b9e0c8a21e43cb69b1.png

四、JDBC的替代产品(Hibernate、MyBatis)

上面介绍的都是手工的连接数据库,写SQL语句。这部分的替代产品会代替我们的这些工作。

替代工具:

Commons-dbutils

Hibernate;

Mybatis;

1、Commons-dbutils

字面的意思可以理解为:通用的数据库工具类。

Apache组织提供的一个开源JDBC工具类库,对传统操作数据库的类进行二次封装,可以把结果集转化为List。

特点:

1 杜绝了资源泄露。修正了JDBC代码并不难,但是这通常导致连接泄露并且难以跟踪到。2 大段的持久化数据到数据库代码彻底精简,剩下的代码清晰地表达了编码的意图。3 不需要手工从ResultSet里set值到JavaBean中,每一行数据都将会以一个Bean示例的形式出现。

核心接口:

1、DbUtils:提供如关闭、装载JDBC驱动程序等常规工作的工具类;

2、QueryRunner:该类简化了Sql查询,它常与ResultSetHandler组合在一起。

3、ResultSetHandler:执行处理一个java.sql.ResultSet,将数据转变并处理为任何一种形式,这样有益于其应用而且使用起来容易。

2、Hibernate

3、Mybatis