日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

JavaWeb:JDBC之数据库连接池

發(fā)布時間:2025/4/16 java 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaWeb:JDBC之数据库连接池 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

JDBC系列閱讀

  • JavaWeb:用JDBC操作數(shù)據(jù)庫
  • JavaWeb:JDBC之事務
  • JavaWeb:JDBC之數(shù)據(jù)庫連接池
  • 使用JDBC實現(xiàn)水果超市管理系統(tǒng)
  • 1. 池參數(shù)(所有池參數(shù)都有默認值)

    • 初始大小:10個
    • 最小空閑連接數(shù):3個
    • 增量:一次創(chuàng)建的最小單位(5個)
    • 最大空閑連接數(shù):12個
    • 最大連接數(shù):20個
    • 最大的等待時間:1000毫秒

    2. 四大連接參數(shù)

    連接池也是使用四大連接參數(shù)來完成創(chuàng)建連接對象!

    3. 實現(xiàn)的接口

    連接池必須實現(xiàn):javax.sql.DataSource接口!

    連接池返回的Connection對象,它的close()方法與眾不同!調(diào)用它的close()不是關閉,而是把連接歸還給池!

    4. 數(shù)據(jù)庫連接池

    4.1 數(shù)據(jù)庫連接池的概念

    用池來管理Connection,這可以重復使用Connection。有了池,所以我們就不用自己來創(chuàng)建Connection,而是通過池來獲取Connection對象。當使用完Connection后,調(diào)用Connection的close()方法也不會真的關閉Connection,而是把Connection“歸還”給池。池就可以再利用這個Connection對象了

    4.2 JDBC數(shù)據(jù)庫連接池接口(DataSource)

    Java為數(shù)據(jù)庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商可以讓自己的連接池實現(xiàn)這個接口。這樣應用程序可以方便的切換不同廠商的連接池!

    4.3 自定義連接池(ItcastPool)

    分析:ItcastPool需要有一個List,用來保存連接對象。在ItcastPool的構造器中創(chuàng)建5個連接對象放到List中!當用人調(diào)用了ItcastPool的getConnection()時,那么就從List拿出一個返回。當List中沒有連接可用時,拋出異常

    我們需要對Connection的close()方法進行增強,所以我們需要自定義ItcastConnection類,對Connection進行裝飾!即對close()方法進行增強。因為需要在調(diào)用close()方法時把連接“歸還”給池,所以ItcastConnection類需要擁有池對象的引用,并且池類還要提供“歸還”的方法

    ItcastPool.java

    public class ItcastPool implements DataSource {private static Properties props = new Properties();private List<Connection> list = new ArrayList<Connection>();static {InputStream in = ItcastPool.class.getClassLoader().getResourceAsStream("dbconfig.properties");try {props.load(in);Class.forName(props.getProperty("driverClassName"));} catch (Exception e) {throw new RuntimeException(e);}}public ItcastPool() throws SQLException {for (int i = 0; i < 5; i++) {Connection con = DriverManager.getConnection(props.getProperty("url"), props.getProperty("username"),props.getProperty("password"));ItcastConnection conWapper = new ItcastConnection(con, this);list.add(conWapper);}}public void add(Connection con) {list.add(con);}public Connection getConnection() throws SQLException {if(list.size() > 0) {return list.remove(0);}throw new SQLException("沒連接了");}...... }

    ItcastConnection.java

    public class ItcastConnection extends ConnectionWrapper {private ItcastPool pool;public ItcastConnection(Connection con, ItcastPool pool) {super(con);this.pool = pool;}@Overridepublic void close() throws SQLException {pool.add(this);} }

    5. DBCP

    5.1 什么是DBCP?

    DBCP是Apache提供的一款開源免費的數(shù)據(jù)庫連接池!

    Hibernate3.0之后不再對DBCP提供支持!因為Hibernate聲明DBCP有致命的缺欠!DBCP因為Hibernate的這一毀謗很是生氣,并且說自己沒有缺欠

    5.2 DBCP的使用

    public void fun1() throws SQLException {BasicDataSource ds = new BasicDataSource();ds.setUsername("root");ds.setPassword("123");ds.setUrl("jdbc:mysql://localhost:3306/mydb1");ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setMaxActive(20);ds.setMaxIdle(10);ds.setInitialSize(10);ds.setMinIdle(2);ds.setMaxWait(1000);Connection con = ds.getConnection();System.out.println(con.getClass().getName());con.close(); }

    5.3 DBCP的配置信息

    下面是對DBCP的配置介紹:

    #基本配置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb1 username=root password=123#初始化池大小,即一開始池中就會有10個連接對象 默認值為0 initialSize=0#最大連接數(shù),如果設置maxActive=50時,池中最多可以有50個連接,當然這50個連接中包含被使用的和沒被使用的(空閑) #你是一個包工頭,你一共有50個工人,但這50個工人有的當前正在工作,有的正在空閑 #默認值為8,如果設置為非正數(shù),表示沒有限制!即無限大 maxActive=8#最大空閑連接 #當設置maxIdle=30時,你是包工頭,你允許最多有20個工人空閑,如果現(xiàn)在有30個空閑工人,那么要開除10個 #默認值為8,如果設置為負數(shù),表示沒有限制!即無限大 maxIdle=8#最小空閑連接 #如果設置minIdel=5時,如果你的工人只有3個空閑,那么你需要再去招2個回來,保證有5個空閑工人 #默認值為0 minIdle=0#最大等待時間 #當設置maxWait=5000時,現(xiàn)在你的工作都出去工作了,又來了一個工作,需要一個工人。 #這時就要等待有工人回來,如果等待5000毫秒還沒回來,那就拋出異常 #沒有工人的原因:最多工人數(shù)為50,已經(jīng)有50個工人了,不能再招了,但50人都出去工作了。 #默認值為-1,表示無限期等待,不會拋出異常。 maxWait=-1#連接屬性 #就是原來放在url后面的參數(shù),可以使用connectionProperties來指定 #如果已經(jīng)在url后面指定了,那么就不用在這里指定了。 #useServerPrepStmts=true,MySQL開啟預編譯功能 #cachePrepStmts=true,MySQL開啟緩存PreparedStatement功能, #prepStmtCacheSize=50,緩存PreparedStatement的上限 #prepStmtCacheSqlLimit=300,當SQL模板長度大于300時,就不再緩存它 connectionProperties=useUnicode=true;characterEncoding=UTF8;useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=50;prepStmtCacheSqlLimit=300#連接的默認提交方式 #默認值為true defaultAutoCommit=true#連接是否為只讀連接 #Connection有一對方法:setReadOnly(boolean)和isReadOnly() #如果是只讀連接,那么你只能用這個連接來做查詢 #指定連接為只讀是為了優(yōu)化!這個優(yōu)化與并發(fā)事務相關! #如果兩個并發(fā)事務,對同一行記錄做增、刪、改操作,是不是一定要隔離它們啊? #如果兩個并發(fā)事務,對同一行記錄只做查詢操作,那么是不是就不用隔離它們了? #如果沒有指定這個屬性值,那么是否為只讀連接,這就由驅(qū)動自己來決定了。即Connection的實現(xiàn)類自己來決定! defaultReadOnly=false#指定事務的事務隔離級別 #可選值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE #如果沒有指定,那么由驅(qū)動中的Connection實現(xiàn)類自己來決定 defaultTransactionIsolation=REPEATABLE_READ

    6. C3P0

    6.1 C3P0簡介

    C3P0也是開源免費的連接池!C3P0被很多人看好!

    6.2 C3P0的使用

    C3P0中池類是:ComboPooledDataSource。

    public void fun1() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource();ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb1");ds.setUser("root");ds.setPassword("123");ds.setDriverClass("com.mysql.jdbc.Driver");ds.setAcquireIncrement(5);ds.setInitialPoolSize(20);ds.setMinPoolSize(2);ds.setMaxPoolSize(50);Connection con = ds.getConnection();System.out.println(con);con.close(); }

    配置文件要求:

    • 文件名稱:必須叫c3p0-config.xml
    • 文件位置:必須在src下

    c3p0也可以指定配置文件,而且配置文件可以是properties,也可騍xml的。當然xml的高級一些了。但是c3p0的配置文件名必須為c3p0-config.xml,并且必須放在類路徑下。

    <?xml version="1.0" encoding="UTF-8"?> <c3p0-config><default-config><property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property><property name="driverClass">com.mysql.jdbc.Driver</property><property name="user">root</property><property name="password">123</property><property name="acquireIncrement">3</property><property name="initialPoolSize">10</property><property name="minPoolSize">2</property><property name="maxPoolSize">10</property></default-config><named-config name="oracle-config"><property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property><property name="driverClass">com.mysql.jdbc.Driver</property><property name="user">root</property><property name="password">123</property><property name="acquireIncrement">3</property><property name="initialPoolSize">10</property><property name="minPoolSize">2</property><property name="maxPoolSize">10</property></named-config> </c3p0-config>

    c3p0的配置文件中可以配置多個連接信息,可以給每個配置起個名字,這樣可以方便的通過配置名稱來切換配置信息。上面文件中默認配置為mysql的配置,名為oracle-config的配置也是mysql的配置,呵呵

    public void fun2() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource();Connection con = ds.getConnection();System.out.println(con);con.close(); }public void fun2() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource("orcale-config");Connection con = ds.getConnection();System.out.println(con);con.close(); }

    7. Tomcat配置連接池

    7.1 Tomcat配置JNDI資源

    JNDI(Java Naming and Directory Interface),Java命名和目錄接口。JNDI的作用就是:在服務器上配置資源,然后通過統(tǒng)一的方式來獲取配置的資源

    我們這里要配置的資源當然是連接池了,這樣項目中就可以通過統(tǒng)一的方式來獲取連接池對象了

    下圖是Tomcat文檔提供的:

    配置JNDI資源需要到<Context>元素中配置<Resource>子元素:

    • name:指定資源的名稱,這個名稱可以隨便給,在獲取資源時需要這個名稱
    • factory:用來創(chuàng)建資源的工廠,這個值基本上是固定的,不用修改
    • type:資源的類型,我們要給出的類型當然是我們連接池的類型了
    • bar:表示資源的屬性,如果資源存在名為bar的屬性,那么就配置bar的值。對于DBCP連接池而言,你需要配置的不是bar,因為它沒有bar這個屬性,而是應該去配置url、username等屬性
    <Context> <Resource name="mydbcp"type="org.apache.tomcat.dbcp.dbcp.BasicDataSource"factory="org.apache.naming.factory.BeanFactory"username="root"password="123"driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1/mydb1"maxIdle="3"maxWait="5000"maxActive="5"initialSize="3"/> </Context> <Context> <Resource name="myc3p0"type="com.mchange.v2.c3p0.ComboPooledDataSource"factory="org.apache.naming.factory.BeanFactory"user="root"password="123"classDriver="com.mysql.jdbc.Driver" jdbcUrl="jdbc:mysql://127.0.0.1/mydb1"maxPoolSize="20"minPoolSize ="5"initialPoolSize="10"acquireIncrement="2"/> </Context>

    7.2 獲取資源

    配置資源的目的當然是為了獲取資源了。只要你啟動了Tomcat,那么就可以在項目中任何類中通過JNDI獲取資源的方式來獲取資源了

    下圖是Tomcat文檔提供的,與上面Tomcat文檔提供的配置資源是對應的。

    獲取資源:

    • Context:javax.naming.Context
    • InitialContext:javax.naming.InitialContext
    • lookup(String):獲取資源的方法,其中”java:comp/env”是資源的入口(這是固定的名稱),獲取過來的還是一個Context,這說明需要在獲取到的Context上進一步進行獲取?!眀ean/MyBeanFactory”對應<Resource>中配置的name值,這回獲取的就是資源對象了
    Context cxt = new InitialContext(); DataSource ds = (DataSource)cxt.lookup("java:/comp/env/mydbcp"); Connection con = ds.getConnection(); System.out.println(con); con.close(); Context cxt = new InitialContext(); Context envCxt = (Context)cxt.lookup("java:/comp/env"); DataSource ds = (DataSource)env.lookup("mydbcp"); Connection con = ds.getConnection(); System.out.println(con); con.close();

    上面兩種方式是相同的效果

    7.3 修改JdbcUtils

    因為已經(jīng)學習了連接池,那么JdbcUtils的獲取連接對象的方法也要修改一下了。

    JdbcUtils.java

    public class JdbcUtils {private static DataSource dataSource = new ComboPooledDataSource();public static DataSource getDataSource() {return dataSource;}public static Connection getConnection() {try {return dataSource.getConnection();} catch (Exception e) {throw new RuntimeException(e);}} }

    8. ThreadLocal

    Thread ->人類
    Runnable -> 任務類

    keyvalue
    thread1aaa
    thread2bbb
    thread3ccc

    8.1 ThreadLocal API

    ThreadLocal類只有三個方法

    返回值方法說明功能描述
    voidset(T value)保存值
    Tget()獲取值
    voidremove()移除值

    8.2 ThreadLocal的內(nèi)部是Map

    ThreadLocal內(nèi)部其實是個Map來保存數(shù)據(jù)。雖然在使用ThreadLocal時只給出了值,沒有給出鍵,其實它內(nèi)部使用了當前線程做為鍵

    class MyThreadLocal<T> {private Map<Thread,T> map = new HashMap<Thread,T>();public void set(T value) {map.put(Thread.currentThread(), value);}public void remove() {map.remove(Thread.currentThread());}public T get() {return map.get(Thread.currentThread());} }

    9. BaseServlet

    9.1 BaseServlet的作用

    在開始客戶管理系統(tǒng)之前,我們先寫一個工具類:BaseServlet

    我們知道,寫一個項目可能會出現(xiàn)N多個Servlet,而且一般一個Servlet只有一個方法(doGet或doPost),如果項目大一些,那么Servlet的數(shù)量就會很驚人

    為了避免Servlet的“膨脹”,我們寫一個BaseServlet。它的作用是讓一個Servlet可以處理多種不同的請求。不同的請求調(diào)用Servlet的不同方法。我們寫好了BaseServlet后,讓其他Servlet繼承BaseServlet,例如CustomerServlet繼承BaseServlet,然后在CustomerServlet中提供add()、update()、delete()等方法,每個方法對應不同的請求。

    9.2 BaseServlet分析

    我們知道,Servlet中處理請求的方法是service()方法,這說明我們需要讓service()方法去調(diào)用其他方法。例如調(diào)用add()、mod()、del()、all()等方法!具體調(diào)用哪個方法需要在請求中給出方法名稱!然后service()方法通過方法名稱來調(diào)用指定的方法

    無論是點擊超鏈接,還是提交表單,請求中必須要有method參數(shù),這個參數(shù)的值就是要請求的方法名稱,這樣BaseServlet的service()才能通過方法名稱來調(diào)用目標方法。例如某個鏈接如下:

    <a href=/xxx/CustomerServlet?method=add”>添加客戶</a>

    9.3 BaseServlet代碼

    public class BaseServlet extends HttpServlet {/** 它會根據(jù)請求中的m,來決定調(diào)用本類的哪個方法*/protected void service(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {req.setCharacterEncoding("UTF-8");res.setContentType("text/html;charset=utf-8");// 例如:http://localhost:8080/demo1/xxx?m=addString methodName = req.getParameter("method");// 它是一個方法名稱// 當沒用指定要調(diào)用的方法時,那么默認請求的是execute()方法。if(methodName == null || methodName.isEmpty()) {methodName = "execute";}Class c = this.getClass();try {// 通過方法名稱獲取方法的反射對象Method m = c.getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);// 反射方法目標方法,也就是說,如果methodName為add,那么就調(diào)用add方法。String result = (String) m.invoke(this, req, res);// 通過返回值完成請求轉(zhuǎn)發(fā)if(result != null && !result.isEmpty()) {req.getRequestDispatcher(result).forward(req, res);}} catch (Exception e) {throw new ServletException(e);}} }

    10. DBUtils

    10.1 DBUtils簡介

    DBUtils是Apache Commons組件中的一員,開源免費!
    DBUtils是對JDBC的簡單封裝,但是它還是被很多公司使用!
    DBUtils的Jar包:dbutils.jar

    10.2 DBUtils主要類

    • DbUtils:都是靜態(tài)方法,一系列的close()方法;
    • QueryRunner:
    • update():執(zhí)行insert、update、delete
    • query():執(zhí)行select語句
    • batch():執(zhí)行批處理

    10.3 QueryRunner

    update()方法

    QueryRunner的update()方法可以用來執(zhí)行insert、update、delete語句。

    • 無參構造QueryRunner()
    //可執(zhí)行增、刪、改語句,需要調(diào)用者提供Connection,這說明本方法不再管理Connection了。支持事務! int update(Connection con, String sql, Object… params); @Testpublic void fun1() throws SQLException {QueryRunner qr = new QueryRunner();String sql = "insert into user values(?,?,?)";qr.update(JdbcUtils.getConnection(), sql, "u1", "zhangSan", "123");}

    還有另一種方式來使用QueryRunner,QueryRunner(DataSource)帶連接池的構造,這種方式在創(chuàng)建QueryRunner時傳遞了連接池對象,那么在調(diào)用update()方法時就不用再傳遞Connection了

    //可執(zhí)行增、刪、改語句 int update(String sql, Object… params); @Testpublic void fun2() throws SQLException {QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());String sql = "insert into user values(?,?,?)";qr.update(sql, "u1", "zhangSan", "123");}

    query()方法

    //可執(zhí)行查詢,它會先得到ResultSet,然后調(diào)用rsh的handle()把rs轉(zhuǎn)換成需要的類型! T query(String sql, ResultSetHandler rsh, Object... params);//支持事務 T query(Connection con, String sql, ResultSetHadler rsh, Object... params);

    10.4 ResultSetHandler

    我們知道在執(zhí)行select語句之后得到的是ResultSet,然后我們還需要對ResultSet進行轉(zhuǎn)換,得到最終我們想要的數(shù)據(jù)。你可以希望把ResultSet的數(shù)據(jù)放到一個List中,也可能想把數(shù)據(jù)放到一個Map中,或是一個Bean中

    DBUtils提供了一個接口ResultSetHandler,它就是用來ResultSet轉(zhuǎn)換成目標類型的工具。你可以自己去實現(xiàn)這個接口,把ResultSet轉(zhuǎn)換成你想要的類型

    DBUtils提供了很多個ResultSetHandler接口的實現(xiàn),這些實現(xiàn)已經(jīng)基本夠用了,我們通常不用自己去實現(xiàn)ResultSet接口了

    處理器功能描述
    MapHandler單行處理器!把結果集轉(zhuǎn)換成Map<String,Object>,其中列名為鍵
    MapListHandler多行處理器!把結果集轉(zhuǎn)換成List<Map<String,Object>>
    BeanHandler單行處理器!把結果集轉(zhuǎn)換成Bean,該處理器需要Class參數(shù),即Bean的類型
    BeanListHandler多行處理器!把結果集轉(zhuǎn)換成List<Bean>
    ColumnListHandler多行單列處理器!把結果集轉(zhuǎn)換成List<Object>,使用ColumnListHandler時需要指定某一列的名稱或編號,例如:new ColumListHandler(“name”)表示把name列的數(shù)據(jù)放到List中
    ScalarHandler單行單列處理器!把結果集轉(zhuǎn)換成Object。一般用于聚集查詢,例如select count(*) from tab_student


    Map處理器

    Bean處理器

    Column處理器

    Scalar處理器

    10.5 QueryRunner之查詢

    QueryRunner的查詢方法是:

    public <T> T query(String sql, ResultSetHandler<T> rh, Object… params) public <T> T query(Connection con, String sql, ResultSetHandler<T> rh, Object… params)

    query()方法會通過sql語句和params查詢出ResultSet,然后通過rh把ResultSet轉(zhuǎn)換成對應的類型再返回

    @Test public void fun1() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select * from tab_student where number=?";Map<String,Object> map = qr.query(sql, new MapHandler(), "S_2000");System.out.println(map); }@Test public void fun2() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select * from tab_student";List<Map<String,Object>> list = qr.query(sql, new MapListHandler());for(Map<String,Object> map : list) {System.out.println(map);} }@Test public void fun3() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select * from tab_student where number=?";Student stu = qr.query(sql, new BeanHandler<Student>(Student.class), "S_2000");System.out.println(stu); }@Test public void fun4() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select * from tab_student";List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));for(Student stu : list) {System.out.println(stu);} }@Test public void fun5() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select * from tab_student";List<Object> list = qr.query(sql, new ColumnListHandler("name"));for(Object s : list) {System.out.println(s);} }@Test public void fun6() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "select count(*) from tab_student";Number number = (Number)qr.query(sql, new ScalarHandler());int cnt = number.intValue();System.out.println(cnt); }

    10.6 QueryRunner之批處理

    QueryRunner還提供了批處理方法:batch()

    我們更新一行記錄時需要指定一個Object[]為參數(shù),如果是批處理,那么就要指定Object[][]為參數(shù)了。即多個Object[]就是Object[][]了,其中每個Object[]對應一行記錄:

    @Test public void fun10() throws SQLException {DataSource ds = JdbcUtils.getDataSource();QueryRunner qr = new QueryRunner(ds);String sql = "insert into tab_student values(?,?,?,?)";Object[][] params = new Object[10][];//表示 要插入10行記錄for(int i = 0; i < params.length; i++) {params[i] = new Object[]{"S_300" + i, "name" + i, 30 + i, i%2==0?"男":"女"};}qr.batch(sql, params); }

    11. Service事務

    在Service中使用ThreadLocal來完成事務,為將來學習Spring事務打基礎!

    11.1 DAO中的事務

    在DAO中處理事務真是“小菜一碟”。

    public void xxx() {Connection con = null;try {con = JdbcUtils.getConnection();con.setAutoCommitted(false);QueryRunner qr = new QueryRunner();String sql = …;Object[] params = …;qr.update(con, sql, params);sql = …;Object[] params = …;qr.update(con, sql, params);con.commit(); } catch(Exception e) {try {if(con != null) {con.rollback();} } catch(Exception e) {} } finally {try {con.close(); } catch(Exception e) {} } }

    11.2 Service才是處理事務的地方

    我們要清楚一件事,DAO中不是處理事務的地方,因為DAO中的每個方法都是對數(shù)據(jù)庫的一次操作,而Service中的方法才是對應一個業(yè)務邏輯。也就是說我們需要在Service中的一方法中調(diào)用DAO的多個方法,而這些方法應該在一起事務中。

    怎么才能讓DAO的多個方法使用相同的Connection呢?方法不能再自己來獲得Connection,而是由外界傳遞進去。

    public void daoMethod1(Connection con, …) { } public void daoMethod2(Connection con, …) { }

    在Service中調(diào)用DAO的多個方法時,傳遞相同的Connection就可以了。

    public class XXXService() {private XXXDao dao = new XXXDao();public void serviceMethod() {Connection con = null;try {con = JdbcUtils.getConnection();con.setAutoCommitted(false);dao.daoMethod1(con, …);dao.doaMethod2(con, …);com.commint();} catch(Exception e) {try {con.rollback();} catch(Exception e) {}} finally {try {con.close();} catch(Exception e) {}}} }

    但是,在Service中不應該出現(xiàn)Connection,它應該只在DAO中出現(xiàn),因為它是JDBC的東西,JDBC的東西是用來連接數(shù)據(jù)庫的,連接數(shù)據(jù)庫是DAO的事兒!!!但是,事務是Service的事兒,不能放到DAO中!!!

    11.3 修改JdbcUtils

    我們把對事務的開啟和關閉放到JdbcUtils中,在Service中調(diào)用JdbcUtils的方法來完成事務的處理,但在Service中就不會再出現(xiàn)Connection這一“禁忌”了。

    DAO中的方法不用再讓Service來傳遞Connection了。DAO會主動從JdbcUtils中獲取Connection對象,這樣,JdbcUtils成為了DAO和Service的中介!

    我們在JdbcUtils中添加beginTransaction()和rollbackTransaction(),以及commitTransaction()方法。這樣在Service中的代碼如下:

    public class XXXService() {private XXXDao dao = new XXXDao();public void serviceMethod() {try {JdbcUtils.beginTransaction();dao.daoMethod1(…);dao.daoMethod2(…);JdbcUtils.commitTransaction();} catch(Exception e) {JdbcUtils.rollbackTransaction();}} } DAO public void daoMethod1(…) {Connection con = JdbcUtils.getConnection(); } public void daoMethod2(…) {Connection con = JdbcUtils.getConnection(); }

    在Service中調(diào)用了JdbcUtils.beginTransaction()方法時,JdbcUtils要做準備好一個已經(jīng)調(diào)用了setAuthCommitted(false)方法的Connection對象,因為在Service中調(diào)用JdbcUtils.beginTransaction()之后,馬上就會調(diào)用DAO的方法,而在DAO方法中會調(diào)用JdbcUtils.getConnection()方法。這說明JdbcUtils要在getConnection()方法中返回剛剛準備好的,已經(jīng)設置了手動提交的Connection對象。

    在JdbcUtils中創(chuàng)建一個Connection con屬性,當它為null時,說明沒有事務!當它不為null時,表示開啟了事務。

    • 在沒有開啟事務時,可以調(diào)用“開啟事務”方法;
    • 在開啟事務后,可以調(diào)用“提交事務”和“回滾事務”方法;
    • getConnection()方法會在con不為null時返回con,再con為null時,從連接池中返回連接。

    beginTransaction()

    判斷con是否為null,如果不為null,就拋出異常!
    如果con為null,那么從連接池中獲取一個Connection對象,賦值給con!然后設置它為“手動提交”。

    getConnection()

    判斷con是否為null,如果為null說明沒有事務,那么從連接池獲取一個連接返回;
    如果不為null,說明已經(jīng)開始了事務,那么返回con屬性返回。這說明在con不為null時,無論調(diào)用多少次getConnection()方法,返回的都是同個Connection對象。

    commitTransaction()

    判斷con是否為null,如果為null,說明沒有開啟事務就提交事務,那么拋出異常;
    如果con不為null,那么調(diào)用con的commit()方法來提交事務;
    調(diào)用con.close()方法關閉連接;
    con = null,這表示事務已經(jīng)結束!

    rollbackTransaction()

    判斷con是否為null,如果為null,說明沒有開啟事務就回滾事務,那么拋出異常;
    如果con不為null,那么調(diào)用con的rollback()方法來回滾事務;
    調(diào)用con.close()方法關閉連接;
    con = null,這表示事務已經(jīng)結束!

    JdbcUtils.java

    public class JdbcUtils {private static DataSource dataSource = new ComboPooledDataSource();private static Connection con = null;public static DataSource getDataSource() {return dataSource;}public static Connection getConnection() throws SQLException {if(con == null) {return dataSource.getConnection();}return con;}public static void beginTranscation() throws SQLException {if(con != null) {throw new SQLException("事務已經(jīng)開啟,在沒有結束當前事務時,不能再開啟事務!");}con = dataSource.getConnection();con.setAutoCommit(false);}public static void commitTransaction() throws SQLException {if(con == null) {throw new SQLException("當前沒有事務,所以不能提交事務!");}con.commit();con.close();con = null;}public static void rollbackTransaction() throws SQLException {if(con == null) {throw new SQLException("當前沒有事務,所以不能回滾事務!");}con.rollback();con.close();con = null; } }

    11.4 再次修改JdbcUtils

    現(xiàn)在JdbcUtils有個問題,如果有兩個線程!第一個線程調(diào)用了beginTransaction()方法,另一個線程再調(diào)用beginTransaction()方法時,因為con已經(jīng)不再為null,所以就會拋出異常了。

    我們希望JdbcUtils可以多線程環(huán)境下被使用!這說明最好的方法是為每個線程提供一個Connection,這樣每個線程都可以開啟自己的事務了。
    還記得ThreadLocal類么?

    public class JdbcUtils {// 配置文件的默認配置!要求你必須給出c3p0-config.xml!!!private static ComboPooledDataSource dataSource = new ComboPooledDataSource();// 它是事務專用連接!private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();/*** 使用連接池返回一個連接對象* @return* @throws SQLException*/public static Connection getConnection() throws SQLException {Connection con = tl.get();// 當con不等于null,說明已經(jīng)調(diào)用過beginTransaction(),表示開啟了事務!if(con != null) return con;return dataSource.getConnection();}/*** 返回連接池對象!* @return*/public static DataSource getDataSource() {return dataSource;}/*** 開啟事務* 1. 獲取一個Connection,設置它的setAutoComnmit(false)* 2. 還要保證dao中使用的連接是我們剛剛創(chuàng)建的!* --------------* 1. 創(chuàng)建一個Connection,設置為手動提交* 2. 把這個Connection給dao用!* 3. 還要讓commitTransaction或rollbackTransaction可以獲取到!* @throws SQLException*/public static void beginTransaction() throws SQLException {Connection con = tl.get();if(con != null) throw new SQLException("已經(jīng)開啟了事務,就不要重復開啟了!");/** 1. 給con賦值!* 2. 給con設置為手動提交!*/con = getConnection();//給con賦值,表示事務已經(jīng)開始了con.setAutoCommit(false);tl.set(con);//把當前線程的連接保存起來!}/*** 提交事務* 1. 獲取beginTransaction提供的Connection,然后調(diào)用commit方法* @throws SQLException*/public static void commitTransaction() throws SQLException {Connection con = tl.get();//獲取當前線程的專用連接if(con == null) throw new SQLException("還沒有開啟事務,不能提交!");/** 1. 直接使用con.commit()*/con.commit();con.close();// 把它設置為null,表示事務已經(jīng)結束了!下次再去調(diào)用getConnection()返回的就不是con了tl.remove();//從tl中移除連接}/*** 提交事務* 1. 獲取beginTransaction提供的Connection,然后調(diào)用rollback方法* @throws SQLException*/public static void rollbackTransaction() throws SQLException {Connection con = tl.get();if(con == null) throw new SQLException("還沒有開啟事務,不能回滾!");/** 1. 直接使用con.rollback()*/con.rollback();con.close();tl.remove();}/*** 釋放連接 * @param connection* @throws SQLException*/public static void releaseConnection(Connection connection) throws SQLException {Connection con = tl.get();/** 判斷它是不是事務專用,如果是,就不關閉!* 如果不是事務專用,那么就要關閉!*/// 如果con == null,說明現(xiàn)在沒有事務,那么connection一定不是事務專用的!if(con == null) connection.close();// 如果con != null,說明有事務,那么需要判斷參數(shù)連接是否與con相等,若不等,//說明參數(shù)連接不是事務專用連接if(con != connection) connection.close();} }

    11.5 轉(zhuǎn)賬示例

    public class AccountDao {public void updateBalance(String name, double balance) throws SQLException {String sql = "update account set balance=balance+? where name=?";Connection con = JdbcUtils.getConnection();QueryRunner qr = new QueryRunner();qr.update(con, sql, balance, name);} } public class AccountService {private AccountDao dao = new AccountDao();public void transfer(String from, String to, double balance) {try {JdbcUtils.beginTranscation();dao.updateBalance(from, -balance);dao.updateBalance(to, balance);JdbcUtils.commitTransaction();} catch(Exception e) {try {JdbcUtils.rollbackTransaction();} catch (SQLException e1) {throw new RuntimeException(e);}}} } AccountService as = new AccountService(); as.transfer("zs", "ls", 100); 《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的JavaWeb:JDBC之数据库连接池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 日本视频在线免费观看 | 欧美日韩一区二区三区69堂 | 中国大陆一级毛片 | 99热这里只有精品4 精品国产黄色 | 一级特黄aa大片欧美 | 夜夜狠狠 | 欧美视频在线观看一区二区 | 精一区二区| 女18毛片| www.97ai.com| 亚洲在线视频网站 | 欧美国产日韩一区二区 | 朝桐光在线视频 | exo妈妈mv在线播放高清免费 | 麻豆精品在线看 | 影音先锋成人资源网站 | 快色av| 97插插插| 又欲又污又肉又黄短文 | 中文字幕丝袜诱惑 | 爱的天堂 | 女人被狂躁60分钟视频 | 欧美日韩在线免费播放 | 欧美少妇18p | 久99精品 | 欧美大奶在线 | 99色影院 | 性视频在线播放 | 先锋影音av资源在线 | 精品动漫3d一区二区三区免费版 | www午夜视频 | 福利视频不卡 | 韩国三级中文字幕 | 99精品视频在线看 | 操久久久| 毛茸茸多毛bbb毛多视频 | 九色国产 | 亚洲精华液一区二区 | 国产网友自拍视频 | 双腿张开被9个男人调教 | 亚洲人成电影在线播放 | 日韩少妇激情 | 久久久久久午夜 | www.日本高清 | 人人爽在线 | 怨女1988国语版在线观看高清 | 男人的天堂色偷偷 | 九九九精品视频 | 成人激情小说网站 | 国产一级二级毛片 | 精品动漫一区二区 | 国产寡妇亲子伦一区二区三区四区 | 午夜婷婷丁香 | 蜜桃av免费在线观看 | 国产特级av | 围产精品久久久久久久 | 国产爆乳无码一区二区麻豆 | 日韩视频免费观看高清完整版 | 成人一级影视 | www..com黄色| 国产伦精品一区 | 美女扒开腿让男生桶 | 99自拍网| 国产剧情av麻豆香蕉精品 | 91视频com| 韩国三级hd中文字幕的背景音乐 | 中文字幕日韩欧美在线 | 青青草狠狠干 | 日韩精品一区二区三区免费视频 | 国产一区二区三区自拍 | 成人亚洲一区二区 | 亚洲影院在线观看 | 亚洲色图13p | 超碰干| 综合久色| 日本九九视频 | 91视频在线观看 | 不卡久久 | 国产欧美日韩另类 | 人人艹人人 | 国产亚洲在线 | 老司机av导航 | 日韩午夜激情视频 | 色在线免费 | 亚洲无吗一区二区三区 | 日本免费一区二区三区四区五六区 | 国产视频第一区 | 欧美疯狂做受xxxxx高潮 | 91小宝寻花一区二区三区 | 精品亚洲一区二区三区四区五区 | 亚洲美女视频网站 | 久久九九久精品国产免费直播 | 欧美一区二区三区久久久 | 一级特黄色片 | 午夜在线免费观看视频 | 波多一区二区 | 免费日韩视频 | 韩国伦理在线看 | 欧美91精品久久久久国产性生爱 |