• 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)    获得指定列的类型


  • 相关阅读:
    阿里云oss大文件切片上传
    轻量全景查看器 pannellum初探
    基于.NetCore开发博客项目 StarBlog (15) 生成随机尺寸图片
    GitLab的安装、配置、使用
    Django数据库性能优化之 使用Python集合操作
    合阔智云核心生产系统切换到服务网格 ASM 的落地实践
    应用发布新版本如何保障流量无损
    传统 Web 框架部署与迁移
    云原生可观测套件:构建无处不在的可观测基础设施
    鱼传科技:函数计算,只要用上就会觉得香
  • 原文地址:https://www.cnblogs.com/angel11288/p/eedff8630297b4d49fcacdbe3879b7ed.html
Copyright © 2020-2023  润新知