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();
在数据库中新建存储过程:
注意:创建的存储过程名称不要加"()",不然在调用存储过程会出错。
代码示例:
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
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
运行结果:
3、Jdbc调用含输入参数存储过程
新建一个过程:
如果输入空白字符,会全部显示,结果如下:
如果传入一个'蔡',则结果如下:
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
运行结果:
4、Jdbc调用含输出参数存储过程
建立一个过程
调用存储过程:显示出一共多少记录,结果如下:
含有输出参数的存储过程
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 }
运行结果:
二、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 }
运行结果:
案例二:
DBCPUtil.java
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
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
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
运行结果:
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 }
运行结果:
3、连接池总结
四、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