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

相关链接: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 package produceDao;
 2 
 3 import java.sql.CallableStatement;
 4 import java.sql.Connection;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 
 8 import sql.Dao.DBUtil;
 9 
10 public class ProduceDAO {
11     public void select_nofilter() throws SQLException{
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 package producetest;
 2 
 3 import java.sql.SQLException;
 4 import produceDao.ProduceDAO;
 5 
 6 public class JdbcProduceTest {
 7     public static void main(String []args) throws SQLException{
 8         ProduceDAO dao=new ProduceDAO();
 9         dao.select_nofilter();
10     }
11 }
JdbcProduceTest.java

运行结果:

          

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

新建一个过程:

        

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

           

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

 1 //只有输入in,没有输出的存储过程
 2     public List<Goddess> select_filter(String sp_name) throws SQLException{
 3         List<Goddess> 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=new Goddess();
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         return result;
23     }

 测试:

 1 public class JdbcProduceTest {
 2     public static void main(String []args) throws SQLException{
 3         ProduceDAO dao=new ProduceDAO();
 4         //dao.select_nofilter();
 5         String sp_name="白";
 6         List<Goddess> res=null;
 7         res=dao.select_filter(sp_name);
 8         for(int i=0;i<res.size();i++){
 9             System.out.println(res.get(i).getId()+":"+res.get(i).getUserName()+":"+res.get(i).getAge());
10         }
11     }
12 }

运行结果:

          

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

 建立一个过程

         

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

          

含有输出参数的存储过程

 1 //Jdbc调用含有输出参数的的存储过程
 2     public Integer select_count()throws SQLException{
 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         return count;
11     }

 测试:

 1 public class JdbcProduceTest {
 2     public static void main(String []args) throws SQLException{
 3         ProduceDAO dao=new ProduceDAO();
 4 //        //dao.select_nofilter();
 5 //        String sp_name="蔡";
 6 //        List<Goddess> res=null;ss
 7 //        res=dao.select_filter(sp_name);
 8 //        for(int i=0;i<res.size();i++){
 9 //            System.out.println(res.get(i).getId()+":"+res.get(i).getUserName()+":"+res.get(i).getAge());
10 //        }
11         String sp_name="蔡";
12         List<Goddess> 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<Goddess> select_filter(String sp_name)throws SQLException{
24         ProduceDAO dao=new ProduceDAO();
25         return dao.select_filter(sp_name);
26     }
27     public static Integer select_count()throws SQLException{
28         ProduceDAO dao=new ProduceDAO();
29         return dao.select_count();
30     }
31     public static void showResult(List<Goddess> result){
32         for(int i=0;i<result.size();i++){
33             System.out.println(result.get(i).getId()+":"+result.get(i).getUserName()+":"+result.get(i).getAge());
34         }
35     }
36     
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";
 2 private 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万个连接这样的话就会对数据库造成很大的压力,如果数据库承受不了的话就崩溃了,服务器也崩溃了,网站就瘫痪了。

即:

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

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

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

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

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

  1. dbcp
  2. 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.Driver
 2 url=jdbc:mysql://localhost:3306/mydb
 3 username=root
 4 password=mysql123
 5  
 6 maxIdle=30
 7 maxIdle=10
 8 maxWait=1000
 9 
10 removeAbandoned=true
11 removeAbandonedTimeout=180

  3、配置并测试dbcp连接

案例一:dbcptest.java

 1 package db;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 
 6 import org.apache.commons.dbcp.BasicDataSource;
 7 
 8 public class testjdbc {
 9     public static void main(String []args) throws SQLException{
10         BasicDataSource dataSource=null;
11         dataSource=new BasicDataSource();
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 package db;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 import java.util.Properties;
 6 
 7 import javax.sql.DataSource;
 8 
 9 import org.apache.commons.dbcp.BasicDataSource;
10 import org.apache.commons.dbcp.BasicDataSourceFactory;
11 
12 public class DBCPUtil {
13     private static DataSource DS;
14     private static final String configFile="dbcp.properties";
15     
16     /**
17      * 从数据源获得一个连接
18      * @throws SQLException 
19      * */
20     
21     public Connection getConn() throws SQLException,Exception{
22         Connection conn=null;
23         if(DS!=null){
24             conn=DS.getConnection();//从数据源里拿到连接
25             conn.setAutoCommit(false);//关闭连接的自动提交
26             return conn;    
27         }
28         return conn;
29     }
30     
31     
32     /**
33      * 默认的构造函数
34      * @throws Exception 
35      * **/
36     public DBCPUtil() throws Exception{
37         initDbcp();
38     }
39     
40     private static void initDbcp() throws Exception{
41         Properties pops=new Properties();
42         pops.load(DBCPUtil.class.getClassLoader().getResourceAsStream(configFile));
43         
44         DS=BasicDataSourceFactory.createDataSource(pops);//通过BasicDataSourceFactory提供的工厂类,拿到DataSource数据源
45     }
46     
47     //构造函数,初始化了DS,指定数据库
48     public DBCPUtil(String url){
49         initDS(url);
50     }
51     
52     //构造函数,初始化了DS,指定了所有参数
53     public DBCPUtil(String connectURL,String username,String password,String driverClass,int initialSize, 
54             int maxIdle,int minIdle,int maxWait){
55         initDS(connectURL,username,password,driverClass,initialSize,maxIdle,minIdle,maxWait);
56     }
57     
58     public static void initDS(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,int maxWait){
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) throws SQLException{
 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=new Goddess();
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         return goddess;
28     }
29 //查询单个女神(根据id去查询,DBCP连接池的方式)
30     public Goddess getByDbcp(Integer id) throws Exception{
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=new Goddess();
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         return goddess;
58     }
GoddessDao.java

测试:DbcpTest.java

 1 package db;
 2 
 3 import java.sql.SQLException;
 4 
 5 import java.util.Date;
 6 
 7 import sql.Dao.GoddessDao;
 8 import sql.model.Goddess;
 9 public class DbcpTest {
10     public static void main(String []args) throws Exception{
11         //1、通过普通的方式操作数据库
12         Date a=new Date();
13         get();
14         Date b=new Date();
15         System.out.println(b.getTime()-a.getTime());
16         
17         //2、通过DBCP连接池的方式操作数据库
18         Date c=new Date();
19         getByDbcp();
20         Date d=new Date();
21         System.out.println(d.getTime()-c.getTime());
22         
23         //通过运行发现,第一种方式要比第二种方式花费时间要多
24         
25     }
26 
27     private static void get() throws SQLException {
28         // TODO Auto-generated method stub
29         GoddessDao dao=new GoddessDao();
30         Goddess goddess=dao.get(2);
31         System.out.println("普通连接:"+goddess.toString());
32     }
33     private static void getByDbcp() throws Exception {
34         // TODO Auto-generated method stub
35         GoddessDao dao=new GoddessDao();
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 <c3p0-config>
 3     <named-config name="helloC3p0">
 4        <property name="driverClass">com.mysql.jdbc.Driver</property>
 5        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mydb</property> 
 6        <property name="user">root</property>
 7        <property name="password">mysql123</property>
 8        
 9        <property name="acquireIncrement">50</property>
10        <!-- 若数据库中连接数不足时,一次向数据库服务器申请多少个链接。 -->
11        
12        <property name="initialPoolSize">5</property>
13        <!-- 初始化数据库连接时,连接的个数 -->
14        
15        <property name="minPoolSize">50</property>
16        <!-- 数据库连接池中的最小的数据库连接数 -->
17        <property name="maxPoolSize">1000</property>
18        <!-- 数据库连接池中的最大的数据库连接数 -->
19        
20        <property name="maxStatements">20</property>
21        <!-- c3p0数据库连接池可以使用的Statement对象的个数 -->
22        <property name="maxStatementsPerConnection">5</property>
23        <!-- c3p0数据库连接池每个连接可以使用的Statement对象的个数 -->
24     </named-config>
25 </c3p0-config>

  3、测试

实例一:C3P0Test.java

 1 package c3p0;
 2 
 3 import javax.sql.DataSource;
 4 
 5 import com.mchange.v2.c3p0.ComboPooledDataSource;
 6 
 7 public class C3poTest {
 8     private static final String helloC3p0="c3p0-config.xml";
 9     public static void main(String []args) throws Exception{
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,将数据转变并处理为任何一种形式,这样有益于其应用而且使用起来容易。

具体的参考  :Apache的DBUtils框架学习

2、Hibernate

具体参考: 新手上路第一个Hibernate例子。 以及  Hibernate框架学习

3、Mybatis

具体参考: mybatis实战教程

转载:JAVA数据库连接--JDBC调用存储过程,事务管理和高级应用

当神已无能为力,那便是魔渡众生
原文地址:https://www.cnblogs.com/liuzhongfeng/p/6057155.html