10第十一天JDBC事务控制管理

  1. create database day11;
  2. use day11;
  3. create table account (
  4. id int primary key auto_increment,
  5. name varchar(20),
  6. money double
  7. );
  8. insert into account values(null,'a',1000),(null,'b',1000);

一、事务
    1、事务的概念:事务是指逻辑上的一组操作,这组操作要么同时完成要么同时不完成。
      
    2、 事务的管理:默认情况下,数据库会自动管理事务,管理的方式是一条语句就独占一个事务.
                如果需要自己控制事务也可以通过如下命令开启/提交/回滚事务
                       start transaction;  开启事务
                       commit;  提交事务 
                       rollback;  回滚事务   还原执行一句sql命令之前状态。
                eg:A——B转帐,对应于如下两条sql语句
  1. mysql> select * from account;
  2. +----+------+-------+
  3. | id | name | money |
  4. +----+------+-------+
  5. | 1 | a | 1000 |
  6. | 2 | b | 1000 |
  7. +----+------+-------+

                   update account set money=money-100 where name=‘a’;
                   update account set money=money+100 where name=‘b’;
                如果第二句话未执行,数据库崩溃了,变成:
  1. mysql> select * from account;
  2. +----+------+-------+
  3. | id | name | money |
  4. +----+------+-------+
  5. | 1 | a | 900 |
  6. | 2 | b | 1000 |
  7. +----+------+-------+
   使用start transaction; 进入事务中,若在事务中执行第一句,事务中内容发生改变,而再次其他窗口进入数据库,发现数据库内容未变。只有commit;提交事务后才会改变数据库。
    3、当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
     JDBC中管理事务:
          conn.setAutoCommit(false);  设置为不自动提交事务,作为同一个事务
          conn.commit();
          conn.rollback();
          //设置事务回滚点
              SavePoint sp = conn.setSavePoint();
              conn.rollback(sp);
              conn.commit();//回滚后必须要提交
         
  1. package com.lmd.transaction;
  2. import java.sql.Connection/PreparedStatement/ResultSet;
  3. import java.sql.SQLException/Savepoint;
  4. import com.lmd.util.JDBCUtils;
  5. public class JDBCTranDemo {
  6. public static void main(String[] args) {
  7. Connection conn = null;
  8. PreparedStatement ps = null;
  9. ResultSet rs = null;
  10. Savepoint sp = null;
  11. try {
  12. conn = JDBCUtils.getConn();
  13. conn.setAutoCommit(false);
  14. //第一次转账
  15. ps = conn.prepareStatement("update account set money=money-100 where name=?");
  16. ps.setString(1, "a");
  17. ps.executeUpdate();
  18. //int i = 1/0;
  19. ps = conn.prepareStatement("update account set money=money+100 where name=?");
  20. ps.setString(1, "b");
  21. ps.executeUpdate();
  22. //第二次转账,若此次出异常,为了保存第一次转账,
  23. //可以在此处设置一个回滚点
  24. sp = conn.setSavepoint();
  25. ps = conn.prepareStatement("update account set money=money-100 where name=?");
  26. ps.setString(1, "a");
  27. ps.executeUpdate();
  28. //此处遇到异常,执行回滚操作
  29. String s = null;
  30. s.toUpperCase();
  31. ps = conn.prepareStatement("update account set money=money+100 where name=?");
  32. ps.setString(1, "b");
  33. ps.executeUpdate();
  34. conn.commit();
  35. } catch (Exception e) {
  36. try {
  37. //前面运行出异常,进入回滚
  38. if (sp == null) {
  39. conn.rollback();
  40. } else {
  41. //不是null,回滚到回滚点
  42. conn.rollback(sp);
  43. conn.commit();
  44. }
  45. } catch (SQLException e1) {
  46. e1.printStackTrace();
  47. }
  48. e.printStackTrace();
  49. } finally {
  50. JDBCUtils.close(rs, ps, conn);
  51. }
  52. }
  53. }
  
    3、!!!事务的四大特性:一个事务具有的最基本的特性,一个设计良好的数据库可以帮我们保证事务具有这四大特性(ACID):
      (1)、 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
      (2)、 一致性(Consistency):如果事务执行之前数据库是一个完整性的状态,那么事务结束后,无论事务是否执行成功,数据库仍然是一个完整性状态.。
            数据库的完整性状态:当一个数据库中的所有的数据都符合数据库中所定义的所有的约束,此时可以称数据库是一个完整性状态.
      (3)、 隔离性(Isolation):事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
      (4)、 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
        
    4、隔离性详解
        (1)、将数据库设计成单线程的数据库,可以防止所有的线程安全问题,自然就保证了隔离性。但是如果数据库设计成这样,那么效率就会极其低下

        如果是两个线程并发修改,一定会互相捣乱,这时必须利用锁机制防止多个线程的并发修改
        如果两个线程并发查询,没有线程安全问题。
        如果两个线程一个修改,一个查询......有些场景有问题,有些没有。如下:

            如果不考虑隔离性,可能会引发如下问题:
            1)、脏读:一个事务读取到另一个事务未提交的数据。
            2)、不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同 --- 行级别的问题。
            3)、虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致 --- 表级别的问题。
               1)、脏读:一个事务读取到另一个事务未提交的数据
  1.             a 1000
  2.             b 1000     
  3.             ----------
  4.             a:
  5.                 start transaction;
  6.                 update account set money=money-100 where name=a;
  7.                 update account set money=money+100 where name=b;
  8.             ----------
  9.             b:
  10.                 start transaction;
  11.                 select * from account;                 
  12.                     a : 900
  13.                     b : 1100
  14.             ----------
  15.             a:
  16.                 rollback;
  17.             ----------
  18.             b:
  19.                 start transaction;
  20.                 select* from account;
  21.                     a: 1000
  22.                     b: 1000
      
              2)、不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同 --- 行级别的问题
      和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
  1.             a: 1000  1000  1000
  2.             b: 银行职员  
  3.             ---------
  4.             b:start transaction;
  5.             select 活期存款 from account where name='a'; ---- 活期存款:1000
  6.             select 定期存款 from account where name='a'; ---- 定期存款:1000
  7.             select 固定资产 from account where name='a'; ---- 固定资产:1000       
  8.                 -------
  9.                 a:
  10.                     start transaction;
  11.                     update accounset set 活期=活期-1000 where name='a';
  12.                     commit;
  13.                 -------
  14.             select 活期+定期+固定 from account where name='a';  --- 总资产:2000
  15.             commit;
  16.             ----------

                3)、虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致 --- 表级别的问题
  1.             a: 1000
  2.             b: 1000
  3.             d: 银行业务人员      
  4.             -----------
  5.             d:
  6.                 start transaction;
  7.                 select sum(money) from account; --- 2000
  8.                 select count(name) from account; --- 2           
  9.                 ------
  10.                 c:
  11.                     start transaction;
  12.                         insert into account values(c,4000);
  13.                      commit;
  14.                 ------ 
  15.                 select sum(money)/count(name) from account; --- 平均:2000元/个
  16.                 commit;
  17.             ------------    

    5、set [global/session]  transaction isolation level 设置事务隔离级别
            select @@tx_isolation    查询当前事务隔离级别
       四大隔离级别:
                read uncommitted -- 不防止任何隔离性问题,具有脏读/不可重复度/虚读(幻读)问题
                read committed -- 可以防止脏读问题,但是不能防止不可重复度/虚读(幻读)问题
                repeatable read -- 可以防止脏读/不可重复读问题,但是不能防止虚读(幻读)问题
                serializable -- 数据库被设计为单线程数据库,可以防止上述所有问题,但效率低下
        
                从安全性上考虑:Serializable>Repeatable read>read committed>read uncommitted
                从效率上考虑:read uncommitted>read committed>Repeatable read>Serializable
                
                真正使用数据的时候,根据自己使用数据库的需求,综合分析对安全性和对效率的要求,选择一个隔离级别使数据库运行在这个隔离级别上。
                mysql 默认下就是Repeatable read隔离级别
                oracle 默认下就是read committed个隔离级别
                
                查询当前数据库的隔离级别:select @@tx_isolation;
                设置隔离级别:set [global/session] transaction isolation level xxxx;
                       其中如果不写默认是session指的是修改当前客户端和数据库交互时的隔离级别;
                       而如果使用global,则修改的是数据库的默认隔离级别。
1.mysql -u root -p
2.set global transaction isolation level read uncommitted;
3.set transaction isolation level serializable;
4.select @@tx_isolation;
        先打开一个cmd命令窗口,输入1、2和4回车,关闭;新打开两个窗口:
一个窗口输入1、4,如下1:            另一个窗口输入1、3,如下2:
 模拟脏读:  在2中开启事务,改变数据,不提交;窗口1可以读取数据库改变后的数据;而窗口2进行回滚,窗口2又看到原始数据。     
  1. 演示不同隔离级别下的并发问题
  2. set transaction isolation level 设置事务隔离级别
  3. select @@tx_isolation 查询当前事务隔离级别
  4. 1.当把事务的隔离级别设置为read uncommitted时,会引发脏读、不可重复读和虚读
  5. A窗口
  6. set transaction isolation level read uncommitted;
  7. start transaction;
  8. select * from account;
  9. -----发现a帐户是1000元,转到b窗口
  10. B窗口
  11. start transaction;
  12. update account set money=money+100 where name='aaa';
  13. -----不要提交,转到a窗口查询
  14. select * from account
  15. -----发现a多了100元,这时候a读到了b未提交的数据(脏读)
  16. 2.当把事务的隔离级别设置为read committed时,会引发不可重复读和虚读,但避免了脏读
  17. A窗口
  18. set transaction isolation level read committed;
  19. start transaction;
  20. select * from account;
  21. -----发现a帐户是1000元,转到b窗口
  22. B窗口
  23. start transaction;
  24. update account set money=money+100 where name='aaa';
  25. commit;
  26. -----转到a窗口

        数据库中的锁机制:
            共享锁:在非Serializable隔离级别做查询不加任何锁,而在Serializable隔离级别下做的查询加共享锁。          
                    共享锁的特点:共享锁和共享锁可以共存,但是共享锁和排他锁不能共存
            排他锁:在所有隔离级别下进行增删改的操作都会加排他锁,
                    排他锁的特点:和任意其他锁都不能共存

        以上为更新丢失问题:
            两个线程基于同一个查询结果进行修改,后修改的人会将先修改人的修改覆盖掉。(以下两种解决方案)
               悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题,在每次查询时就加上排他锁。
                    select * from xxx for update;
               乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失,利用一个版本字段进行控制。
                           
                  查询非常多,修改非常少,使用乐观锁
                  修改非常多,查询非常少,使用悲观锁
 

========================================================================================
二、数据库连接池
 
     1、数据库连接池编写原理分析 连接池 数据源
       (1)、编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
               Connection getConnection()
               Connection getConnection(String username, String password)
       (2)、实现DataSource接口,并实现连接池功能的步骤:
               在DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中
               实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。
               当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。
   2、编写数据库连接池核心
   (1)、扩展Connection的close方法
               在关闭数据库连接时,将connection存回连接池中,而并非真正的关闭
   (2)、扩展类的三种方式
               基于继承--- 方法覆盖
               使用装饰模式包装类,增强原有行为
               使用动态代理 --- 基于字节码Class在内存中执行过程

手写连接池:
改造conn的close方法
   继承
   装饰
   !动态代理
  1. public class MyPool implements DataSource {
  2. private static List<Connection> pool = new LinkedList<Connection>();
  3. static{
  4. try {
  5. Class.forName("com.mysql.jdbc.Driver");
  6. for (int i = 0; i < 5; i++) {
  7. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day11", "root", "666666");
  8. pool.add(conn);
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. @Override
  15. public Connection getConnection() throws SQLException {
  16. if (pool.size()==0) {
  17. for (int i = 0; i < 5; i++) {
  18. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day11", "root", "666666");
  19. pool.add(conn);
  20. }
  21. }
  22. //return pool.remove(0);
  23. Connection conn = pool.remove(0);
  24. //--利用动态代理改造close方法
  25. Connection proxy = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
  26. @Override
  27. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  28. if ("close".equals(method.getName())) {
  29. //想改造的方法,自己写
  30. returnConn(conn);
  31. return null;
  32. } else {
  33. //不想改造的方法,调用被代理者身上相同的方法
  34. return method.invoke(conn, args);
  35. }
  36. }
  37. });
  38. System.err.println("获取了一个连接,池里还剩余"+pool.size()+"个连接!");
  39. return proxy;
  40. }
  41. public void returnConn(Connection conn) {
  42. try {
  43. if (conn!=null && !conn.isClosed()) {
  44. pool.add(conn);
  45. System.out.println("返回了一个连接,池里还剩余"+pool.size()+"个连接!");
  46. }
  47. } catch (SQLException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. //其他重写方法省写
  52. }
简陋版连接池:
  1. import java.sql.Connection;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import com.lmd.pool.MyPool;
  6. public class JDBCDemo {
  7. public static void main(String[] args) {
  8. Connection conn = null;
  9. PreparedStatement ps = null;
  10. MyPool pool = new MyPool();
  11. ResultSet rs = null;
  12. try {
  13. conn = pool.getConnection();
  14. ps = conn.prepareStatement("select * from account");
  15. rs = ps.executeQuery();
  16. while (rs.next()) {
  17. String name = rs.getString("name");
  18. System.out.println(name);
  19. }
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. } finally {
  23. if (rs!=null) {
  24. try {
  25. rs.close();
  26. } catch (SQLException e) {
  27. e.printStackTrace();
  28. } finally {
  29. rs = null;
  30. }
  31. }
  32. if (ps!=null) {
  33. try {
  34. ps.close();
  35. } catch (SQLException e) {
  36. e.printStackTrace();
  37. } finally {
  38. ps = null;
  39. }
  40. }
  41. if (conn!=null) {
  42. try {
  43. conn.close();
  44. } catch (SQLException e) {
  45. e.printStackTrace();
  46. } finally {
  47. conn = null;
  48. }
  49. }
  50. }
  51. }
  52. }
  53. //获取了一个连接,池里还剩余4个连接!
  54. //a
  55. //b
  56. //c
  57. //返回了一个连接,池里还剩余5个连接!
三、开源数据库连接池(DataSource)
     (1)、现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
     (2)、也有一些开源组织提供了数据源的独立实现:
           DBCP 数据库连接池
           C3P0 数据库连接池
           Apache Tomcat内置的连接池(apache dbcp)
     (3)、实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
1、DBCP数据源
    (1)、DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
           Commons-dbcp.jar:连接池的实现            commons-dbcp-1.4.jar
           Commons-pool.jar:连接池实现的依赖库   commons-pool-1.5.6.jar
    (2)、Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
开源数据源:
DBCP:
 方式1:
  1. BasicDataSource source = new BasicDataSource();
  2. source.setDriverClassName("com.mysql.jdbc.Driver");
  3. source.setUrl("jdbc:mysql:///day11");
  4. source.setUsername("root");
  5. source.setPassword("root");
  6. conn = source.getConnection();
 方式2:
  1. Properties prop = new Properties();
  2. prop.load(new FileReader("dbcp.properties"));
  3. BasicDataSourceFactory factory = new BasicDataSourceFactory();
  4. DataSource source = factory.createDataSource(prop);
配置文件中:  在java工程下
  1. driverClassName=com.mysql.jdbc.Driver
  2. url=jdbc:mysql:///day11
  3. username=root
  4. password=666666
配置设置
  1. #连接设置
  2. driverClassName=com.mysql.jdbc.Driver
  3. url=jdbc:mysql://localhost:3306/jdbc
  4. username=root
  5. password=666666
  6. #<!-- 初始化连接 -->
  7. initialSize=10
  8. #最大连接数量
  9. maxActive=50
  10. #<!-- 最大空闲连接 -->
  11. maxIdle=20
  12. #<!-- 最小空闲连接 -->
  13. minIdle=5
  14. #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
  15. maxWait=60000
  16. #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
  17. #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
  18. connectionProperties=useUnicode=true;characterEncoding=gbk
  19. #指定由连接池所创建的连接的自动提交(auto-commit)状态。
  20. defaultAutoCommit=true
  21. #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
  22. #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
  23. defaultTransactionIsolation=READ_UNCOMMITTED
2、C3P0数据源    c3p0-0.9.1.2.jar    这个要会用
C3P0数据源:
方式1:
  1. ComboPooledDataSource source = new ComboPooledDataSource(["aaa"]);
  2. source.setDriverClass("com.mysql.jdbc.Driver");
  3. source.setJdbcUrl("jdbc:mysql:///day11");
  4. source.setUser("root");
  5. source.setPassword("666666");

方式2:
  1. ComboPooledDataSource source = new ComboPooledDataSource();
在类加载目录(src)下名称为c3p0-config.xml的配置文件中配置:
  1. <c3p0-config> //多个配置时
  2.  <default-config name="aaa">
  3.    <property name="driverClass">com.mysql.jdbc.Driver</property>
  4.    <property name="jdbcUrl">jdbc:mysql:///day11</property>
  5.    <property name="user">root</property>
  6.    <property name="password">666666</property>
  7.  </default-config>
  8. </c3p0-config>

3、tomcat内置的数据源(DBCP):  Apache
      JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,这套API的主要作用在于:它可以把Java对象放在一个容器中(支持JNDI容器 Tomcat),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
       其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
~1.如何为tomcat配置数据源
            (1)、给所有web应用起作用
~tomcat/conf/context.xml文件中配置<Context>配置在这个位置的信息将会被所有的web应用所共享
~tomcat/conf/[enginename]/[Host]/context.xml文件中可以配置<Context>标签,这里配置的信息将会被这台虚拟主机中的所有web应用所共享(引擎名/主机名
                                                                       F: omcat8confCatalinalocalhost)
            (2)、给当前web应用起作用
~tomcat/conf/server.xml文件中的<Host>标签中配置<Context>标签,这是web应用的第一种配置方式,在这个标签中配置的信息将只对当前web应用起作用
~tomcat/conf/[enginename]/[Host]/自己创建一个.xml文件,在这个文件中使用<Context>标签配置一个web应用,这是web应用第二种配置方式,在这个<Context>标签中配置的信息将只会对当前web应用起作用
~web应用还有第三种配置方式:将web应用直接放置到虚拟主机管理的目录.此时可以在web应用的META-INF文件夹下创建一个context.xml文件,在其中可以写<Context>标签进行配置,这种配置信息将只会对当前web应用起作用
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Context>
  3. <Resource name="mySource" auth="Container" type="javax.sql.DataSource"
  4. username="root" password="666666"
  5. driverClassName="com.mysql.jdbc.Driver"
  6. url="jdbc:mysql:///day11"
  7. maxActive="8" maxIdle="4"/>
  8. </Context>

~2.如果在程序中获取这个数据源
想要访问JNDI就必须在Servlet中才能执行下列代码:
  1.               import javax.naming.Context/InitialContext;
  2. Context initCtx = new InitialContext();
  3.       Context jndi = (Context) initCtx.lookup("java:comp/env");
  4.       DataSource source = jndi.lookup("mySource");

1、context.xml配置
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Context>
  3. <Resource name="mySource" auth="Container" 
  4. type="javax.sql.DataSource"
  5. username="root" password="666666"
  6. driverClassName="com.mysql.jdbc.Driver"
  7. url="jdbc:mysql:///day11"
  8. maxActive="8" maxIdle="4"/>
  9. </Context>
2、web.xml配置
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="3.0"
  3. xmlns="http://java.sun.com/xml/ns/javaee"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  6. http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  7. <servlet>
  8. <servlet-name>DataSourceInitServlet</servlet-name>
  9. <servlet-class>com.lmd.init.DataSourceInitServlet</servlet-class>
  10. <load-on-startup>1</load-on-startup><!-- 一启动就加载 -->
  11. 1,2,3,4,5代表的是优先级,值越小,优先级所高
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>DataSourceInitServlet</servlet-name>
  15. <url-pattern>/servlet/DataSourceInitServlet</url-pattern>
  16. </servlet-mapping>
  17. </web-app>
3、DataSourceInitServlet.java
       mysql-connector-java-5.1.40-bin.jar最好放在F: omcat8lib文件夹下
  1. package com.lmd.init;
  2. import java.io.IOException;
  3. import java.sql.Connection/PreparedStatement/ResultSet;
  4. import javax.naming.Context/InitialContext;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet/HttpServletRequest/HttpServletResponse;
  7. import javax.sql.DataSource;
  8. public class DataSourceInitServlet extends HttpServlet {
  9. //DataSource source = null;
  10. public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  11. }
  12. public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  13. doGet(request, response);
  14. }
  15. //servlet一创建就运行init
  16. @Override
  17. public void init() throws ServletException {
  18. try {
  19. Context initCtx = new InitialContext();
  20. Context jndi = (Context) initCtx.lookup("java:comp/env");
  21. DataSource source = (DataSource) jndi.lookup("mySource");
  22. //使用类变量获取
  23. //或者存起来this.getServletContext().setAttribute("", source);
  24. Connection conn = source.getConnection();
  25. PreparedStatement ps = conn.prepareStatement("select * from account");
  26. ResultSet rs = ps.executeQuery();
  27. while (rs.next()) {
  28. String name = rs.getString("name");
  29. System.out.println(name);
  30. }
  31. rs.close();
  32. ps.close();
  33. conn.close();
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. throw new RuntimeException(e);
  37. }
  38. }
  39. }

4、元数据
- DataBaseMetaData
  (1)、元数据:数据库、表、列的定义信息。 
  (2)、 Connection.getMetaData()
  (3)、DataBaseMetaData对象
       getURL():返回一个String类对象,代表数据库的URL。
       getUserName():返回连接当前数据库管理系统的用户名。
       getDriverName():返回驱动驱动程序的名称。
       getPrimaryKeys(String catalog, String schema, String table):返回指定表主键的结果集
       getTables()

- ParameterMetaData
  (1)、PreparedStatement . getParameterMetaData()
               获得代表PreparedStatement元数据的ParameterMetaData对象。
            select * from user where name=? And password=?
  (2)、ParameterMetaData对象
               getParameterCount()    获得指定参数的个数
               getParameterTypeName(int param)    获得指定参数的sql类型

   (3)、getParameterType异常处理
           Parameter metadata not available for the given statement
   (4)、url后面拼接参数        ?generateSimpleParameterMetadata=true
- ResultSetMetaData
  (1)、ResultSet. getMetaData()
         获得代表ResultSet对象元数据的ResultSetMetaData对象。
  (2)、ResultSetMetaData对象
         getColumnCount()    返回resultset对象的列数
         getColumnName(int column)    获得指定列的名称
         getColumnTypeName(int column)    获得指定列的类型


原文地址:https://www.cnblogs.com/angel11288/p/eedff8630297b4d49fcacdbe3879b7ed.html