zoukankan      html  css  js  c++  java
  • 12.JDBC&事务&连接池

    JDBC
    JDBC:java database connectivity SUN公司提供的一套操作数据库的标准规范,通过Java操作数据库
    JDBC与数据库驱动的关系:接口与实现的关系
     

    一、JDBC规范

    掌握四个核心对象:
    1、DriverManager:用于注册驱动
    2、Connection: 表示与数据库创建的连接
    3、Statement: 操作数据库sql语句的对象
    4、ResultSet: 结果集或一张虚拟表
     
    JDBC规范在JDK的java.sql.*、javax.sql.*中(接口)
    数据库厂商提供的驱动:jar文件(实现类)
     

    二、开发JDBC程序

    1、创建数据库表,并向表中添加测试数据
    2、创建java project项目,添加数据库驱动(*.jar)
    3、实现JDBC操作
    (1)加载驱动  
    DriverManager. registerDriver(Driver driver) ;

    Class.forName("com.mysql.jdbc.Driver");
    (2)创建连接Connection
    (3)得到执行sql语句的Statement对象
    (4)执行sql语句,并返回结果
    (5)处理结果
    (6)关闭资源
     1 public void test1() throws Exception {
     2     // 加载驱动,反射
     3     Class.forName("com.mysql.jdbc.Driver");
     4     
     5     // 获取连接Connection,用java.sql.Connection sun的接口,多态,能用接口就不用类,导java.sql包
     6     Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123");
     7     
     8     // 得到执行sql语句的对象Statement,全是接口,实现在jar包中
     9     Statement createStatement = connection.createStatement();
    10     
    11     // 执行sql语句返回结果
    12     ResultSet executeQuery = createStatement.executeQuery("select * from emp");
    13     
    14     // 处理结果
    15     while(executeQuery.next()) {  // 开始指向表头,一次移动一行
    16         System.out.println(executeQuery.getObject(1));  // 数据库都是从1开始,每次取一列
    17         System.out.println(executeQuery.getObject(2)); 
    18         System.out.println(executeQuery.getObject(3)); 
    19     }
    20     
    21     // 关闭资源
    22         executeQuery.close();
    23         createStatement.close();
    24         connection.close();
    25     }
    26 }

    三、JDBC常用的类和接口

    1、java.sql.Drivermanager类

    创建连接

    (1)注册驱动
    DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用
    原因有2个:导致驱动被注册2次,强烈依赖数据库的驱动jar。
    解决办法:Class.forName("com.mysql.jdbc.Driver");   // 反射加载,解耦,不需要编译路径
    (2)与数据库建立连接
    方式一:(常用)
    getConnection(String url, String user, String password)  // 试图建立到给定数据库 URL 的连接
    getConnection("jdbc:mysql://localhost:3306/day06", "root", "123");

    URL:SUN公司与数据库厂商之间的一种协议。

    jdbc:mysql://localhost:3306/db1
     协议 子协议     IP :   端口号 数据库
    mysql:     jdbc:mysql://localhost:3306/day14 或者 jdbc:mysql:///day14(默认本机连接)
    oracle:    jdbc:oracle:thin:@localhost:1521:sid   // thin瘦的,
    方式二:
    getConnection(String url, Properties info);
    1 Properties info = new Properties();//要参考数据库文档
    2 info.setProperty("user", "root");
    3 info.setProperty("password","root");
    方式三:
    getConnection(String url);
    DriverManager.getConnection("jdbc:mysql://localhost:3306/day14?user=root&password=root");

    JUnit测试类

    @Test注解方法,导包
    import org.junit.Test;

    注意:测试方法不能有返回值和参数

    Assert.assertEquals();  // 断言
     

    2、java.sql.Connection接口

    一个连接

    接口的实现在数据库驱动中,所有与数据库交互都是基于连接对象的。

    Statement createStatement(); //创建操作sql语句的对象

    3、java.sql.Statement接口

    操作sql语句,并返回相应结果的对象(车)

    接口的实现在数据库驱动中,用于执行静态 SQL 语句并返回它所生成结果的对象。
    ResultSet executeQuery(String sql)   //根据查询语句返回结果集,只能执行select语句。
    int executeUpdate(String sql)   // 根据执行的DML(insert update delete)语句,返回受影响的行数。
    boolean execute(String sql)   // 此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集,仅当执行select语句,且有返回结果集时返回true, 其它语句都返回false
    CRUD:客户端的增删改查,对应数据库的
    数据库语句在数据库的图形界面里面写,SQL语句在客户端不用加;
     

    4、java.sql.ResultSet接口

    结果集(客户端存表数据的对象)
    (1)封装结果集
    提供一个游标,默认游标指向结果集第一行之前。
    调用一次next(),游标向下移动一行。
    提供一些get方法
     
    封装数据的方法
    Object getObject(int columnIndex); 根据序号取值,索引从1开始
    Object getObject(String ColomnName); 根据列名取值。
     
    将结果集中的数据封装到javaBean中
    java的数据类型与数据库中的类型的关系
    1 byte           tityint
    2 short          smallint
    3 int            int
    4 long           bigint
    5 float          float
    6 double         double
    7 String         char varchar
    8 Date           date
    Clob 数据库中大文本
     1 boolean next()   //  将光标从当前位置向下移动一行
     2 int getInt(int colIndex)  // 以int形式获取ResultSet结果集当前行指定列号值
     3 int getInt(String colLabel)   // 以int形式获取ResultSet结果集当前行指定列名值
     4 float getFloat(int colIndex)  //  以float形式获取ResultSet结果集当前行指定列号值
     5 float getFloat(String colLabel)   // 以float形式获取ResultSet结果集当前行指定列名值
     6 String getString(int colIndex)  //  以String 形式获取ResultSet结果集当前行指定列号值
     7 String getString(String colLabel)  //  以String形式获取ResultSet结果集当前行指定列名值
     8 Date getDate(int columnIndex);
     9 Date getDate(String columnName);
    10 void close()   // 关闭ResultSet 对象

    使用JDBC实现CRUD操作

     1 // 实体类,对应数据库的每一行
     2 public class Emp {
     3     private int id;
     4     private String name;
     5     private String gender;
     6     
     7     public int getId() {
     8         return id;
     9     }
    10     public void setId(int id) {
    11         this.id = id;
    12     }
    13     public String getName() {
    14         return name;
    15     }
    16 
    17     public void setName(String name) {
    18         this.name = name;
    19     }
    20     public String getGender() {
    21         return gender;
    22     }
    23     public void setGender(String gender) {
    24         this.gender = gender;
    25     }
    26     
    27     @Override
    28     public String toString() {
    29         return "Emp [id=" + id + ", name=" + name + ", gender=" + gender + "]";
    30     }
    31 }
     1 public class TestCRUD {
     2     
     3     @Test
     4     public void test() throws Exception {
     5         // 加载驱动,反射
     6         Class.forName("com.mysql.jdbc.Driver");
     7         
     8         // 获取连接Connection,用java.sql.Connection sun的接口,多态,能用接口就不用类,导java.sql包
     9         Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123");
    10         
    11         // 得到执行sql语句的对象Statement,全是接口,实现在jar包中
    12         Statement createStatement = connection.createStatement();
    13         
    14         // 执行sql语句返回结果
    15         ResultSet executeQuery = createStatement.executeQuery("select * from emp");
    16         List<Emp> list = new ArrayList<Emp>();
    17         
    18         // 处理结果
    19         while(executeQuery.next()) {  // 开始指向表头,一次移动一行
    20             Emp emp = new Emp();  // 要是放在外面的话,会造成覆盖显示最后一行数据,因list中存放的是emp的引用,内存地址都一样,以最后修改的一次为结果
    21             emp.setId(executeQuery.getInt("id"));
    22             emp.setName(executeQuery.getString("name"));
    23             emp.setGender(executeQuery.getString("gender"));
    24             list.add(emp);
    25         }
    26         
    27         // 关闭资源
    28         executeQuery.close();
    29         createStatement.close();
    30         connection.close();
    31         
    32         for (Emp emp : list) {
    33             System.out.println(emp);
    34         }
    35     }
    36 }
    5、释放资源
    资源有限,要正确关闭
     
     1 @Test
     2 public void test2() throws Exception {
     3  // 获取连接Connection
     4 Connection connection = null;
     5 // 得到执行sql语句的对象Statement
     6 Statement createStatement = null;
     7 // 执行sql语句返回结果
     8 ResultSet executeQuery = null;
     9 try {
    10     // 加载驱动,反射
    11     Class.forName("com.mysql.jdbc.Driver");
    12 
    13     connection = DriverManager.getConnection(
    14             "jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123");
    15 
    16     createStatement = connection.createStatement();
    17 
    18     executeQuery = createStatement.executeQuery("select * from emp");
    19 
    20     // 处理结果
    21     while (executeQuery.next()) { // 开始指向表头,一次移动一行
    22         System.out.println(executeQuery.getObject(1)); // 数据库都是从1开始,每次取一列
    23         System.out.println(executeQuery.getObject(2));
    24         System.out.println(executeQuery.getObject(3));
    25     }
    26 } catch (Exception e) {
    27     // TODO Auto-generated catch block
    28     e.printStackTrace();
    29 } finally {
    30     // 关闭资源
    31     if (executeQuery != null) {
    32         try {  
    33             executeQuery.close();  // executeQuery中还可能出错,继续try-catch
    34         } catch (Exception e) {
    35             e.printStackTrace();
    36         }
    37         executeQuery = null; // 让资源回收器回收
    38     }
    39 
    40     if (createStatement != null) {
    41         try {
    42             createStatement.close();
    43         } catch (Exception e) {
    44             e.printStackTrace();
    45         }
    46         createStatement = null; // 让资源回收器回收
    47     }
    48 
    49     if (connection != null) {
    50         try {
    51             connection.close();
    52         } catch (Exception e) {
    53             e.printStackTrace();
    54         }
    55         connection = null; // 让资源回收器回收
    56         }
    57     }
    58 
    59 }

    SQL注入:

    由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击

    原理:

    1.在输入时连接一个永远为真的一个值

    2.使用mysql 中的 – 注释

    解决SQL注入:使用PreparedStatement 取代 Statement,对sql语句进行预编译 

    PreparedStatement pst=con.prepareStatement(String sql);    

    PreparedStatement 解决SQL注入原理:运行在SQL中参数以?占位符的方式表示
    select * from user where username = ? and password = ? ;
    将带有?的SQL 发送给数据库完成编译 (不能执行的SQL 带有?的SQL 进行编译 叫做预编译),在SQL编译后发现缺少两个参数
    PreparedStatement 可以将? 代替参数 发送给数据库服务器,因为SQL已经编译过,参数中特殊字符不会当做特殊字符编译,无法达到SQL注入的目的

    四、事务

    事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功

    1、开启事务命令
    1 start transaction   // 开启事务
    2 Rollback //  回滚事务,将数据恢复到事务开始时状态
    3 Commit  //  提交事务,对事务中进行操作,进行确认操作,事务在提交后,数据就不可恢复

    2、mysql管理事务

    方式一 :同时事务管理SQL 语句

    同上开始事务命令

    方式二:数据库中存在一个自动提交变量 ,通过 show variables like '%commit%'; ---- autocommint 值是 on,说明开启自动提交

    关闭自动提交 set autocommit = off / set autocommit = 0

    如果设置autocommit 为 off,意味着以后每条SQL 都会处于一个事务中,相当于每条SQL执行前 都执行 start transaction 

     注意:Oracle中 autocommit 默认就是 off
     
    3、JDBC使用事务
    当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
    1 Connection.setAutoCommit(false); //  相当于start transaction
    2 Connection.rollback();  rollback
    3 Connection.commit();  commit

    4、事物特性 ACID

    (1)原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
    (2)一致性(Consistency)
    事务前后数据的完整性必须保持一致。
    (3)隔离性(Isolation)
    事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
    (4)持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

     
    5、事务隔离级别
    多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

    如果不考虑隔离性,可能会引发如下问题:

    (1)脏读 :指一个事务读取另一个事务未提交的数据

    (2)不可重复读:在一个事务先后两次读取发生数据不一致情况,第二次读取到另一个事务已经提交数据 (强调数据更新 update)

    (3)虚读(幻读) :在一个事务中,第二次读取发生数据记录数的不同 ,读取到另一个事务已经提交数据 (强调数据记录变化 insert )

    (4)丢失更新 :两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了

    解决:数据库内部定义了四种隔离级别,用于解决三种隔离问题

    1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
    2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
    3 Read committed:可避免脏读情况发生(读已提交)
    4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

    mysql中默认的事务隔离级别是 Repeatable read.
    oracle 中默认的事务隔离级别是 Read committed

    操作数据库内部隔离级别
    set session transaction isolation level 设置事务隔离级别
    select @@tx_isolation 查询当前事务隔离级别

    JDBC中指定事务的隔离级别 
    Connection接口中定义事务隔离级别四个常量:
    (1)static int TRANSACTION_READ_COMMITTED
    指示不可以发生脏读的常量;不可重复读和虚读可以发生。
    (2)static int TRANSACTION_READ_UNCOMMITTED
    指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
    (3)static int TRANSACTION_REPEATABLE_READ
    指示不可以发生脏读和不可重复读的常量;虚读可以发生。
    (4)static int TRANSACTION_SERIALIZABLE
    指示不可以发生脏读、不可重复读和虚读的常量。

    通过 void setTransactionIsolation(int level) 设置数据库隔离级别

    6、事务隔离级别总结
    read uncommitted 什么问题也解决不了.
    read committed 可以解决脏读,其它解决不了.
    Repeatable read 可以解决脏读,可以解决不可重复读,不能解决虚读.
    Serializable 它会锁表,可以解决所有问题.

    安全性:serializable > repeatable read > read committed > read uncommitted
    性能 :serializable < repeatable read < read committed < read uncommitted

    结论: 实际开发中,通常不会选择 serializable 和 read uncommitted 
    mysql默认隔离级别 repeatable read ,oracle默认隔离级别 read committed

    7、TheadLocal
    Service层和Dao层保证共用一个Connection,通过方法传参的方式实现
    要是实现接口时无参,用TheadLocal实现保证共用一个Connection

    ThreadLocal可以理解成是一个Map集合
    Map<Thread,Object>
    set方法是向ThreadLocal中存储数据,那么当前的key值就是当前线程对象.
    get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值。

    如果我们是在同一个线程中,只要在任意的一个位置存储了数据,在其它位置上,就可以获取到这个数据

    关于JdbcUtils中使用ThreadLocal
    1.声明一个ThreadLocal

    private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    2.在getConnection()方法中操作

    1 Connection con = tl.get(); 直接从ThreadLocal中获取,第一次返回的是null.
    2 if (con == null) {
    3   // 2.获取连接
    4   con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    5   tl.set(con); //将con装入到ThreadLocal中。
    6 }
    8、丢失更新

    多个事务对同一条记录进行了操作,后提交的事务将先提交的事务操作覆盖了

    解决丢失更新可以采用两种方式:

    1.悲观锁
    悲观锁 (假设丢失更新一定会发生 )

    利用数据库内部锁机制,管理事务
    提供的锁机制
    (1)共享锁

    select * from table lock in share mode(读锁、共享锁)

    (2)排它锁

    select * from table for update (写锁、排它锁)

    update语句默认添加排它锁

    2.乐观锁
    乐观锁 (假设丢失更新不会发生)

    采用程序中添加版本字段解决丢失更新问题

    create table product (
    id int,
    name varchar(20),
    updatetime timestamp
    );
    
    insert into product values(1,'冰箱',null);
    update product set name='洗衣机' where id = 1;

    解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段id,
    与修改时版本字段不一致,说明别人进行修改过数据 (重改)

    五、连接池

    就是创建一个容器,用于装入多个Connection对象,在使用连接对象时,从容器中获取一个Connection,
    使用完成后,在将这个Connection重新装入到容器中。这个容器就是连接池。(DataSource),也叫做数据源.

    我们可以通过连接池获取连接对象.
    优点:节省创建连接与释放连接 性能消耗(连接池中连接起到复用的作用 ,提高程序性能)

    1、自定义连接池
    (1)创建一个MyDataSource类,在这个类中创建一个LinkedList<Connection>
    (2)在其构造方法中初始化List集合,并向其中装入5个Connection对象。
    (3)创建一个public Connection getConnection();从List集合中获取一个连接对象返回.
    (4)创建一个 public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.

    代码问题:
    1.连接池的创建是有标准的.
    在javax.sql包下定义了一个接口 DataSource
    简单说,所有的连接池必须实现javax.sql.DataSource接口,我们的自定义连接池必须实现DataSource接口。

    2.我们操作时,要使用标准,怎样可以让 con.close()它不是销毁,而是将其重新装入到连接池.
    要解决这个问题,其本质就是将Connection中的close()方法的行为改变。
    怎样可以改变一个方法的行为(对方法功能进行增强)
    1.继承
    2.装饰模式
    1.装饰类与被装饰类要实现同一个接口或继承同一个父类
    2.在装饰类中持有一个被装饰类引用
    3.对方法进行功能增强。
    3.动态代理
    可以对行为增强
    Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);

    结论:

    Connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了,不在是销毁,而是重新装入到连接池。

    1.连接池必须实现javax.sql.DataSource接口。
    2.要通过连接池获取连接对象 DataSource接口中有一个 getConnection方法.
    3.将Connection重新装入到连接池 使用Connection的close()方法。

    2、开源连接池
    (1)DBCP(了解)

    DBCP是apache的一个开源连接池。

    要想使用DBCP连接池,要下载jar包
    导入时要导入两个
    commons-dbcp-1.4.jar
    commons-pool-1.5.6.jar

    关于dbcp连接池使用
    1.手动配置(手动编码)

    BasicDataSource bds = new BasicDataSource();
    
    // 需要设置连接数据库最基本四个条件
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql:///day18");
    bds.setUsername("root");
    bds.setPassword("abc");
    
    // 得到一个Connection
    Connection con = bds.getConnection();

    2.自动配置(使用配置文件)

    1 Properties props = new Properties();
    2 FileInputStream fis = new FileInputStream("D:\java1110\workspace\day18_2\src\dbcp.properties");
    3 props.load(fis);
    4 
    5 DataSource ds = BasicDataSourceFactory.createDataSource(props);

    (2)C3P0(必会)
    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。
    目前使用它的开源项目有Hibernate,Spring等。
    c3p0与dbcp区别:

    c3p0有自动回收空闲连接功能,DBCP没有

    c3p0连接池使用
    1.导包
    c3p0-0.9.1.2.jar

    使用
    1.手动

    1 ComboPooledDataSource cpds = new ComboPooledDataSource();
    2 cpds.setDriverClass("com.mysql.jdbc.Driver");
    3 cpds.setJdbcUrl("jdbc:mysql:///day18");
    4 cpds.setUser("root");
    5 cpds.setPassword("abc");

    2.自动(使用配置文件)
    c3p0的配置文件可以是properties也可以是xml.
    c3p0的配置文件如果名称叫做 c3p0.properties or c3p0-config.xml 并且放置在classpath路径下(对于web应用就是classes目录)
    那么c3p0会自动查找。

    注意:我们其时只需要将配置文件放置在src下就可以。

    使用:

    ComboPooledDataSource cpds = new ComboPooledDataSource(); // 它会在指定的目录下查找指定名称的配置文件,并将其中内容加载。

    (3)Tomcat内置连接池

    Tomcat管理连接池

     要想将一个dbcp连接池让 tomcat管理,只需要创建一个context.xml配置文件,在配置文件中
    <Context>
        <Resource name="jdbc/EmployeeDB" auth="Container"
                        type="javax.sql.DataSource" username="root" password="abc"
                        driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day18"
                        maxActive="8" maxIdle="4"/>
    </Context>

    context.xml文件位置

    1.在Tomcat/conf/context.xml 这时这个连接池是给整个服务器使用的。
    2.在Tomcat/conf/Catalina/localhost 这时这个连接池只给localhost虚拟主机使用。
    3.将context.xml文件放置在web应用的META-INF下,只给本项目使用
    注意:如果是全局设置,那么我们需要将数据库驱动放置在tomcat/lib目录下

    从tomcat中获取连接池

    我们在servlet中获取连接池对象,用JNDI技术实现

    1 Context context = new InitialContext();
    2 Context envCtx = (Context)context.lookup("java:comp/env");  // 固定路径
    3 DataSource datasource = (DataSource) envCtx.lookup("jdbc/EmployeeDB");

    JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,
    JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务
    和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是一种命名服务,
    在这种服务里,对象不但有名称,还有属性。

     

    六、元数据

    元数据(metaData)  指数据库中库、表、列的定义信息 
    1.DataBaseMetaData 数据库元数据(了解
    (1)通过DataBaseMetaData获得 数据库连接的基本参数
    获取DataBaseMetaData:Connection接口中定义了一个方法 getMetaData();
    1 getURL()   // 返回一个String类对象,代表数据库的URL。
    2 getUserName()  // 返回连接当前数据库管理系统的用户名。
    3 getDriverName()   // 返回驱动驱动程序的名称。
    4 getPrimaryKeys(String catalog, String schema, String table)  // 返回指定表主键的相关描述    结果集

    每个主键列描述都有以下列:
    TABLE_CAT String => 表类别(可为 null)
    TABLE_SCHEM String => 表模式(可为 null)
    TABLE_NAME String => 表名称
    COLUMN_NAME String => 列名称
    KEY_SEQ short => 主键中的序列号(值 1 表示主键中的第一列,值 2 表示主键中的第二列)。
    PK_NAME String => 主键的名称(可为 null)

    (2)获得数据库、表、列、主键、外键 定义信息
    1 getTables
    2 getColumns
    3 getPrimaryKeys
     
    2、ParameterMetaData 参数元数据
    参数元数据主要用于获取:sql语句中占位符的相关信息.
    PreparedStatement . getParameterMetaData()  // 获得代表PreparedStatement元数据的ParameterMetaData对象
    Select * from user where name=? And password=?

    ParameterMetaData对象

    1 getParameterCount()  // 获得指定参数的个数
    2 getParameterTypeName(int param)   // 获得指定参数的sql类型

    在获取参数类型时会产生异常
    java.sql.SQLException: Parameter metadata not available for the given statement

    解决方案:
    在url后添加参数    jdbc:mysql:///day18?generateSimpleParameterMetadata=true
    添加这个参数后,我们在获取,它的结果也是varchar,

    原因:是mysql驱动的支持问题,Oracle可以

    3、ResultSetMetaData 结果集元数据(重点)
    ResultSet. getMetaData()   // 获得代表ResultSet对象元数据的ResultSetMetaData对象

    ResultSetMetaData对象

    1 getColumnCount()   // 返回resultset对象的列数
    2 getColumnName(int column)   // 获得指定列的名称
    3 getColumnTypeName(int column)  // 获得指定列的类型 

    七、DbUtils

    commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate的公司的首选
     
    1、DbUtils核心

    1.QueryRunner
    它是用于执行sql语句的类。
    (1)query 用于执行select
    (2)update 用于执行update delete insert
    (3)batch 批处理
    2.ResultSetHandler接口
    用于定义结果集的封装
    它提供九个实现类,可以进行不同的封装。
    3.DbUtils
    它提供关于关闭资源以及事务rollback,commit操作

    2、QueryRunner

    单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

    (1)获取QueryRunner

    new QueryRunner()   // 如果是使用这种构造创建的QueryRunner,它的事务是手动控制.
    new QueryRunner(DataSource ds);  // 如果是使用这种构造,它的事务是自动事务,简单说,一条sql一个事务。

    (2)QueryRunner中的三个核心方法

    query
    update
    batch
    对于上述三个方法,它们提供很多重载。
    如果QueryRunner在创建时,没有传递DataSource参数,那么在使用
    query,update,batch方法时,要传递Connection参数
    如果QueryRunner在创建时,传递了Dataource参数,好么在使用
    query,update,batch方法时,不需要传递Connection参数。

    总结:
    1 QueryRunner runner=new QueryRunner();
    2 runner.query(Connection,sql,ResultSetHandler,Object... param);
    3 runner.update(Connection,sql,Object...param);
    4 runner.batch(Connection con,sql,Object[][] objs);
    5                     
    6 QueryRunner runner=new QueryRunner(DataSource ds);
    7 runner.query(sql,ResultSetHandler,Object... param);
    8 runner.update(sql,Object...param);
    9 runner.batch(sql,Object[][] objs);

    3、DbUtils类

    提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的

    public static void close(…) throws java.sql.SQLException   // DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
    public static void closeQuietly(…)  // 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLException。
    public static void commitAndCloseQuietly(Connection conn)   // 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。 
    public static boolean loadDriver(java.lang.String driverClassName)  //这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。

    模仿QueryRunner

     1 1.query方法模仿
     2 public <T> T query(Connection con, String sql, MyResultSetHandler<T> mrs,Object... params) throws SQLException {
     3 
     4         PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement.
     5         // 问题:sql语句中可能存在参数,需要对参数赋值。
     6 
     7         ParameterMetaData pmd = pst.getParameterMetaData();
     8         // 可以得到有几个参数
     9         int count = pmd.getParameterCount();
    10         for (int i = 1; i <= count; i++) {
    11             pst.setObject(i, params[i - 1]);
    12         }
    13 
    14         ResultSet rs = pst.executeQuery(); // 得到了结果集,要将结果集封装成用户想要的对象,但是,工具不可能知道用户需求。
    15 
    16         return mrs.handle(rs);
    17     }
    
    18 2.update方法模仿
    19 public int update(Connection con, String sql, Object... params) throws SQLException {
    20 
    21     PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement.
    22     // 问题:sql语句中可能存在参数,需要对参数赋值。
    23 
    24     ParameterMetaData pmd = pst.getParameterMetaData();
    25     // 可以得到有几个参数
    26     int count = pmd.getParameterCount();
    27     for (int i = 1; i <= count; i++) {
    28         pst.setObject(i, params[i - 1]);
    29     }
    30 
    31     int row = pst.executeUpdate();
    32     // 关闭资源
    33     pst.close();
    34     return row;
    35 }

    4、ResultSetHandler接口

    用于封装结果集
    ResulsetHandler九个实现类(完成常规操作,而不需要自定义结果集封装)
    (1)ArrayHandler:将结果集中第一条记录封装到Object[],数组中的每一个元素就是记录中的字段值。
    (2)ArrayListHandler:将结果集中每一条记录封装到Object[],数组中的每一个元素就是记录中的字段值。再将这些数组装入到List集合。
    (3)BeanHandler(重点):将结果集中第一条记录封装到一个javaBean中。
    (4)BeanListHandler(重点):将结果集中每一条记录封装到javaBean中,再将javaBean封装到List集合.
    (5)ColumnListHandler:将结果集中指定列的值封装到List集合.
    (6)MapHandler: 将结果集中第一条记录封装到Map集合中,集合的 key就是字段名称,value就是字段值
    (7)MapListHandler: 将结果集中每一条记录封装到Map集合中,集合的 key就是字段名称,value就是字段值,再将这些Map封装到List集合
    (8)KeyedHandler:在使用指定的列的值做为一个Map集合的key,值为每一条记录的Map集合封装
    (9)ScalarHandler:进行单值查询 select count(*) from account;
     1 // 使用BeanUtils实现
     2 Object obj = null;
     3 
     4 Map<String, String[]> map = new HashMap<String, String[]>();
     5 
     6 ResultSetMetaData md = rs.getMetaData();
     7 int count = md.getColumnCount();
     8 
     9 if (rs.next()) {
    10     try {
    11         obj = clazz.newInstance();
    12         for (int i = 1; i <= count; i++) {
    13             map.put(md.getColumnName(i),
    14                     new String[] { rs.getString(md.getColumnName(i)) });
    15         }
    16         BeanUtils.populate(obj, map);
    17     } catch (InstantiationException e) {
    18         e.printStackTrace();
    19     } catch (IllegalAccessException e) {
    20         e.printStackTrace();
    21     } catch (InvocationTargetException e) {
    22         e.printStackTrace();
    23     }
    24 
    25 }
    26 
    27 return obj;

    客户信息的CURD操作

    web  表现层、service 业务层、dao 持久层、utils 工具包、domain 实体类(javaBean) 

     // 查询所有客户信息 
    1
    // 1.在success.jsp页面添加连接 2 <a href="${pageContext.request.contextPath}/findAll">查看所有客户信息</a> 3 // 2.在CustomerFindAllServlet中调用service,在service中调用dao,最后得到一个List<Customer> 4 // 3.在showCustomer.jsp页面展示客户信息 5 <c:forEach items="${cs}" var="c"> 6 <tr> 7 <td><input type="checkbox"> 8 </td> 9 <td>${c.id }</td> 10 <td>${c.name}</td> 11 <td>${c.gender }</td> 12 <td>${c.birthday }</td> 13 <td>${c.cellphone }</td> 14 <td>${c.email }</td> 15 <td>${c.preference }</td> 16 <td>${c.type }</td> 17 <td>${c.description }</td> 18 <td><a>编辑</a>&nbsp;&nbsp;&nbsp;<a>删除</a></td> 19 </tr> 20 </c:forEach>
    1 // 删除操作
    2 // 1.在showCustomer.jsp页面的删除连接上添加参数  客户的id               
       <a href="${pageContext.request.contextPath}/delByid?id=${c.id}">删除</a>
    3 // 2.创建一个CustomerDelByIdServlet,获取请求参数,调用service中删除方法.

    删除完成后,需要重新跳转到查询所有的servlet中,再重新查询数据

    response.sendRedirect(request.getContextPath()+"/findAll");
     1 // 编辑
     2 // 1.查询,做回显示
     3 <a href="${pageContext.request.contextPath}/findById?id=${c.id}">编辑</a>
     4 // 1.创建CustomerFindByIdServlet,得到要查询的id,调用service,得到Custonmer对象。
     5 // 2.将customer对象存储到request域,请求转发到customerInfo.jsp页面。
     6 // 3.在customerInfo.jsp页面展示客户信息
     7 // 注意:客户的id不能修改,所以使用<input type="hidden">
     8 
     9 // 2.修改
    10 // 1.注意使用BeanUtils时的类型转换问题
    11 // 2.注意编码问题    
    12 // post:request.setCharacterEncoding("utf-8");
    13 // get:手动转换  new String(request.getParameter(name).getBytes("iso8859-1"),"utf-8");
    14 
    15 // 3.进行修改操作
    16 String sql = "update customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=? where id=?";
    17 // 修改完成后,在重新查询一次
    18 response.sendRedirect(request.getContextPath() + "/findAll");

    解决关于回显示时的问题

    性别 应该使用radio

     1 // 使用自定义标签
     2 // 1.定义标签类   extends SimpleTagSupport
     3 // 2.定义tld文件
     4     <tag>
     5         <name>sex</name><!-- 标签名称 -->
     6         <tag-class>cn.itcast.customer.tag.GenderTag</tag-class><!-- 标签类 -->
     7         <body-content>empty</body-content><!-- 标签体中内容 -->
     8 
     9         <attribute>
    10             <name>gender</name> <!-- 属性名称 -->
    11             <required>true</required> <!-- 属性必须有 -->
    12             <rtexprvalue>true</rtexprvalue><!-- 属性值可以接收el表达式 -->
    13         </attribute>
    14     </tag>
    15 // 3.在页面上使用
    16     // 1.使用taglib导入
    17     // 2.使用
    18         <my:sex gender="${c.gender}" />

    使用虚拟主机可以将项目部署成顶级域名

    1.在service.xml文件

    1.端口修改为80

    2. 配置主机

    1  <Host name="www.customer.com"  appBase="D:java1110workspaceday19_2"
    2                     unpackWARs="true" autoDeploy="true">  
    3         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    4          prefix="localhost_access_log." suffix=".txt"
    5         pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    6         <Context path="" docBase="D:java1110workspaceday19_2WebRoot" />
    7 </Host>

    3.在hosts文件中配置

     127.0.0.1  www.customer.com
     
     
     
     
     
    有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
  • 相关阅读:
    多线程-工作组模式
    iOS端架构、基本组成与使用说明
    iOS,Core Animation--负责视图的复合功能
    Container Views
    IOS UIView 01-View开始深入 绘制像素到屏幕上
    View Programming Guide for iOS
    UI绘图与信息、样式
    iOS绘图框架CoreGraphics分析
    iOS开发系列--打造自己的“美图秀秀”
    Array与NSArray关系
  • 原文地址:https://www.cnblogs.com/1989guojie/p/6127766.html
Copyright © 2011-2022 走看看