zoukankan      html  css  js  c++  java
  • Java EE笔记 (1) JDBC

    JDBC(全称为:Java Data Base Connectivity)
    共两个包:
    java.sql
    javax.sql
    工作流程:
    应用程序—JDBC—MySQL驱动或Oracle驱动—MySQL数据库或Oracle数据库
    MySQL驱动:mysql-connector-java-5.0.8-bin.jar
    Oracle驱动:ojdbc14.jar

    一、JDBC工作六大步骤:
    1.加载驱动
    DriverManager.registerDriver("com.mysql.jdbc.Driver") 
    不推荐,这种形式会造成JVM虚似机内存中产生两个一样的Driver对象。
    Class.forName("com.mysql.jdbc.Driver");
    推荐这种方式,不会对具体的驱动类产生依赖。

    2.建立连接
    Connection conn = DriverManager.getConnection(url,user,pass); //user与pass为登陆数据库的用户名与密码
    也可采用这种方式
    Connection conn = DriverManager.getConnection(url?user=root&&password=root); //用户名与密码以参数带在后面
    协议:子协议://主机:端口/数据库名
    MySQL的url:jdbc:mysql://localhost:3306/数据库名
    Oracle的url:jdbc:oracle:thin:@localhost:1521:数据库名
    SQLServer的url:jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=数据库名

    3.创建执行SQL的语句
    (1)采用Statement实现,有被SQL注入的危险,如:('or 1=1 or name=') ,且可能造成数据库缓冲区溢出:
    Statement st = conn.createStatement();

    (2)采用PreparedStatement实现,此类安全,可对SQL进行预编译:
    String sql = "insert into 表名(id,name,birthday,mytext,myblob) values(?,?,?,?,?);
    PreparedStatement st = conn.prepareStatement(sql);

    conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//该常量指示生成的键可以获取。
    st.setInt(1,1);  //对应第1个?号
    st.setString(2,"wudi");  //对应第2个?号
    //对应第3个?号,日期存入数据库时要转成sql类型的Date,getTime()方法将日期转成毫秒数字
    st.setDate(3, new java.sql.Date(new java.util.Date().getTime()));  
    //对应第4个?号,存大文本
    URL url = 类.class.getClassLoader().getResource("a.txt");//得到文件虚拟地址
    File file = new File(url.getPath()); //得到文件真实路径并用file连接
    st.setCharacterStream(4, new FileReader(file), (int) file.length());
    //对应第5个?号,存二进制文件
    URL url = 类.class.getClassLoader().getResource("a.jpg");//得到文件虚拟地址
    File file = new File(url.getPath()); //得到文件真实路径并用file连接
    st.setBinaryStream(2, new FileInputStream(file), (int) file.length());  //MySql数据库存的方式

    4.执行语句
    ResultSet rs = st.getGeneratedKeys();  //得到插入行的主键,只对insert有用
    ResultSet rs = st.excute(sql);       //任意操作
    ResultSet rs = st.excuteQuery(sql);  //查询时
    ResultSet rs = st.excuteUpdate(sql); //增删改时
    //可实现批处理sql语句
    Statement.addBatch(sql)  
    优点:可以向数据库发送多条不同的sql语句。
    缺点:sql语句没有预编译
    PreparedStatement.addBatch(sql)
    优点:发送的是预编译后的SQL语句,执行效率高。
    缺点:只能应用在sql语句相同,但参数不同的批处理中。

    (1)小例子:采用Preparedstatement.addBatch(sql)方法实现sql语句批处理,
    String sql = "insert into tbatch(id,name,password) values(?,?,?)";
    st = conn.prepareStatement(sql);
    for(int i=1;i<500003;i++){
      st.setInt(1, i);
      st.setString(2, "aa" + i);
      st.setString(3, "1111" + i);
      //程序执行到此,st对象里面已经是一条完整的sql,可以加到 batch里面去了
      st.addBatch();
      if(i%1000==0){//batch最大可容1000条sql语句
          st.executeBatch();  //执行来1次batch里的所有sql,
          st.clearBatch();  //清空batch里的所有sql
      }
    }

    (2)小例子:在Oracle数据库中实现存二进制文件,注意:这些操作需开启事务。
    //1.向列中存一个指针
    String sql = "insert into tblob(id,image) values(1,empty_blob())";
    PreparedStatement st = conn.prepareStatement(sql);
    st.executeUpdate();
    //2.把指针查询出来
    sql = "select 二制文件列名 from 表 where id=1 for update";  //for update是为了锁定这一行,为了不产生并发冲突
    st = conn.prepareStatement(sql);
    rs = st.executeQuery();
    if(rs.next()){
      BLOB blob =  (BLOB) rs.getBlob("image");
      OutputStream out = blob.getBinaryOutputStream();
      URL url = 类.class.getClassLoader().getResource("a.jpg");//得到文件虚拟地址
      File file = new File(url.getPath()); //得到文件真实路径并用file连接
      FileInputStream in = new FileInputStream(file);
      byte buffer[] = new byte[1024];
      int len = 0;
      while((len=in.read(buffer))>0){
         out.write(buffer, 0, len);
      }
    }
    st.executeBatch();

    5.处理执行结果
    rs.Previous()://移动到前一行
    rs.absolute(1)://移动到指定行
    rs.beforeFirst():移动ResultSet的最前面(表头)
    rs.afterLast() ://移动到ResultSet的最后面(表尾)
    if(rs.next){    //指向第一条,下次执行指向第二行
    第一种方法,得到的是个Object对象
       rs.getObject("id");   //取出该行列名为id的值
       rs.getObject(1);  //取出该行第一列的值
    第二种方法,得到的是对应类型的值
       rs.getBoolean()       //BIT(数据库中对应的类型)
       rs.getByte()          //TINYINT
       rs.getShort()         //SMALLINT
       rs.getInt()           //int
       rs.getLong()          //BIGINT
       rs.getString()        //CHAR,VARCHAR,LONGVARCHAR
       //用流方式取出数据库中的大文本 //Text,longText
       Reader reader = rs.getCharacterStream("大文本列名"或列号);
       或
       Reader reader = rs.getClob("大文本列名"或列号).getCharacterStream();
       char buffer[] = new char[1024];
       int len = 0;
       FileWriter writer = new FileWriter("c:\存放取出数据的文件名.txt");
       while((len = reader.read(buffer))>0){
          writer.write(buffer,0,len);
       }
       //用流方式取出数据库中的二进制文件 //Blob,LongBlob
       InputStream in = rs.getBinaryStream("二进制文件存放列名"或列号);
       或
       InputStream in = rs.getBlob("二进制文件存放列名"或列号).getBinaryStream();
       byte buffer[] = new byte[1024];
       int len = 0;
       FileOutputStream out = new FileOutputStream("c:\1.jpg");
       while((len=in.read(buffer))>0){
           out.write(buffer,0,len);
       }
       rs.getDate()          //DATE,得到的是java.sql.Date类型
       rs.getTime()          //TIME,得到的是java.sql.Time类型
       rs.getTimestamp()     //TIMESTAMP,得到的是java.sql.Timestamp类型
    }

    6.释放资源(以下代码要放在finally中)
    rs.close();
    st.close();
    conn.close();

    二、数据分页显示(customer工程):

    MySql分页sql语句
    select * from customer limit ?,?
    Oracle分页sql语句
    select * from(
        select rownum r_, row_.*  from(
    select * from student order by id
        )row_ where rownum <=5  //5为起始索引位置
    )where r_>=1  //1为结束索引位置

    1.完成分页的dao支持
    1.获取总纪录数的方法
    2.根据起始位置,得到某一页数据的方法

    2、写响应用户分页显示的servlet
    1.调用dao获得总纪录数
    2.得到用户查看哪一页
    3.把上面两个信息,传递给page,并使用page对象负责完成计算逻辑
    4.根据page计算出来的该页在数据库的起始位置,凋用dao获得页面数据
    5.把封装了页面数据的list集合,封装到page对象中
    6.把page对象,保存到request域中,交给jsp显示

    3、jsp显示page对象数据
    1.要显示page里面封装的list集合(即页面数据)
    2.foreach循环显示page里面封装的开始页码和结束页码,以在jsp页面中显示页号

    4、page对象完成什么计算逻辑?
    1.要根据总纪录数,算出总页数
    2.要根据用户所要查看页,算出该页的数据应该从数据库哪个位置开始取
    3.根据用户所要查看的页,计算jsp页面中,页号的起始码和结束码
    4.page对象提供一个list集合,用于封装页面数据
    修改的做法
    1.回显用户的数据
    1.点修改,访问servlet:FindCustomer,它负责取出需要修改的用户的数据
    2.FindCustomer这个servlet把用户数据交给jsp回显
    3.用户可以在jsp回显页面上修改用户数据,提交给EditCoustomer,EditCoustomer这个servlet负责用最新的用户数据,覆盖原有数据库的数据

    三、JDBC调用存储过程步骤:

    1.得到CallableStatement,并调用存储过程
    CallableStatement cs = conn.prepareCall("{call 方法(?, ?)}");

    2.设置参数,注册返回值,得到输出
    cs.registerOutParameter(2, Types.VARCHAR);
    cs.setString(1, "abc");
    cs.execute();
    cs.getString(2);

    四、手动写一个连接池

    1.写一个自已的类实现java.sql.DataSource接口

     

    五、常用数据库连接池(也叫数据源),实现了DataSoruce

    1.DBCP 是 Apache 软件基金组织下的开源连接池实现
    在工程中增加如下两个 jar 文件:
    Commons-dbcp.jar:连接池的实现
    Commons-pool.jar:连接池实现的依赖库
    //dbcpconfig.properties为DBCP的配置文件
    InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
    Properties prop = new Properties();
    prop.load(in);
    BasicDataSourceFactory factory = new BasicDataSourceFactory();
    DataSource ds = factory.createDateSource(prop);  //得到数据源对象
    ds.getConnection();  //返回Connection,供自已手写的dao调用
    ds.getDataSource();  //DataSource,供DBUtils框架调用

    2.C3P0
    在工程中增加如下一个 jar 文件:
    c3p0-0.9.1.2.jar:连接池的实现
    ComBolDataSource ds = new ComboPooledDataSource();
    ds.setDriverClass("com.mysql.jdbc.Driver");  //装载mysql数据库驱动
    ds.setJdbcUrl("jdbc:mysql://localhost:3306/数据库名"); //注册
    ds.setUser("数据库用户名");  
    ds.setPassword("数据库密码");
    ds.setMaxPoolSize(50);  //设置最大连接数
    ds.setMinPoolSize(10);  //设置最小连接数
    ds.setInitialPoolSize(20); //设置初始连接数
    ds.getConnection();  //返回Connection,供自已手写的dao调用
    ds.getDataSource();  //DataSource,供DBUtils框架调用

    3.Tomcat的连接池
    (1)、此种配置下,驱动jar文件需放置在tomcat的lib下
    在WebRoot/META-INF/建军一个context.xml文件,写如下代码:
    <Context>
      <Resource name="jdbc/任意名称a"  //为了让程序可以拿到这个配置文件
                auth="Container"
                type="javax.sql.DataSource"
                username="数据库用户名"
                password="数据库密码"
                driverClassName="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/数据库名"
                maxActive="8"
                maxIdle="4"/>
    </Context>

    (2)、用JNDI(Java Naming and Directory Interface)技术拿到Tomcat的连接池:
    在javax.naming包下,其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
    Context initCtx = new InitialContext(); //jndi初始化
    Context envCtx = (Context) initCtx.lookup("java:comp/env");//得到tomcat中的JNDI容器
    DataSource ds = (DataSource) envCtx.lookup("jdbc/任意名称a"); //拿到了context.xml中配置的数据库连接池
    ds.getConnection();  //返回Connection,供自已手写的dao调用
    ds.getDataSource();  //DataSource,供DBUtils框架调用

     

    六、事务

    1.事务的概念:
    事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
    数据库开启事务命令
    start transaction  开启事务
    Rollback  回滚事务
    Commit   提交事务
    JDBC控制事务语句
    //关闭JDBC默认提交方式,让多条SQL在一个事务中执行
    Connection.setAutoCommit(false);  
    //设置事务回滚点
    Savepoint sp = conn.setSavepoint();
    //回滚到上面设置的回滚点
    Connection.rollback(sp);
    //提交,回滚后必须要提交
    Connection.commit();

    2.事务的特性(ACID):
    原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。?
    一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
    隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
    持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

    3.事务的隔离性:
    脏读:指一个事务读取了另外一个事务未提交的数据。
    不可重复读:在一个事物内读取表中的某一行数据,多次读取结果不同。
    和脏读的区别是:脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
    虚读:是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
    数据库共定义了四种隔离级别:
    Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)    例:conn.TRANSACTION_SERIALIZABLE
    Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)
    Read committed:可避免脏读情况发生(读已提交)。
    Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
    数据库存中对事务隔离性的操作语句:
    set   transaction isolation level 设置事务隔离级别
    select @@tx_isolation查询当前事务隔离级别

    4.程序中设置隔离级别(conn为Connection类),此操作要在开启事务之前:
    conn.setTransactionIsolation(conn.TRANSACTION_SERIALIZABLE);
    conn.setTransactionIsolation(conn.TRANSACTION_REPEATABLE_READ);
    conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);
    conn.setTransactionIsolation(conn.TRANSACTION_READ_UNCOMMITTED);

     

    七、元数据-DataBaseMetaData

    1.返回数据库的定义信息:
    DataBaseMetaData db = Connection.getDatabaseMetaData():返回数据库元数据对象
    db.getURL():返回一个String类对象,代表数据库的URL。
    db.getUserName():返回连接当前数据库管理系统的用户名。
    db.getDatabaseProductName():返回数据库的产品名称。
    db.getDatabaseProductVersion():返回数据库的版本号。
    db.getDriverName():返回驱动驱动程序的名称。
    db.getDriverVersion():返回驱动程序的版本号。
    db.isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。

    2.返回表的定义信息:
    ParameterMetaData pd = PreparedStatement.getParameterMetaData():返回代表PreparedStatement对象的元数据
    pd.getParameterCount():获得PreparedStatement对象传递的参数个数
    pd.getParameterType(1):获得PreparedStatement对象传递的参数中第1个参数的sql类型

    3.返回列的定义信息:
    ResultSetMetaData rd = ResultSet.getMetaData():获得代表ResultSet对象的元数据对象
    rd.getColumnCount():返回resultset对象的列数
    rd.getColumnName(1):获得第1列的列名
    rd.getColumnTypeName(1):获得第1列的列数据的类型 

    八、DBUtils框架

    共一个包:
    org.apache.commons.dbutils
    导jar包:commons-dbutils.jar
    DbUtils类的方法(都是静态的):
    DbUtils.close(conn,st,rs):关闭Connection、Statement和ResultSet。
    DbUtils.closeQuietly(conn,st,rs):关闭Connection、Statement和ResultSet,隐藏异常。
    DbUtils.commitAndCloseQuietly(conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。 
    DbUtils.loadDriver():装载并注册JDBC驱动程序,如果成功就返回true,不需要捕捉ClassNotFoundException异常。
    QueryRunner类的主要方法:
    runner.query(conn,sql,params,ResultSetHandler):查询操作,将结果集保存到指定JavaBean对象中
    runner.query(sql,params,ResultSetHandler):查询操作,将结果集保存到指定JavaBean对象中
    runner.query(conn,sql,ResultSetHandler): 查询操作,不用参数,将结果集保存到指定JavaBean对象中。
    runner.update(conn,sql,params):用来执行一个更新(插入、更新或删除)操作。
    runner.update(conn,sql):用来执行一个不需要置换参数的更新操作。
    ResultSetHandler 接口的实现类:
    ArrayHandler:把结果集中的第一行数据转成对象数组。
    ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
    BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
    BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
    ColumnListHandler:将结果集中某一列的数据存放到List中。
    KeyedHandler(name):将结果集中的每一行数据都封装到一个小Map里,再把这些小map再存到一个另一大map里,其map的key为传进来的key,如name。
    MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
    MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List

    1.增删改操作(CUD):
    QueryRunner runner = new QueryRunner(DataSource类);  //得到QueryRunner对象
    String sql = "sql语句";  //这里的语句可以是CRUD语句
    Object[] arr = {参数列表}; //构建参数对象数组
    qr.update(sql,arr);   //将参数传给DBUtils框架,执行增删改操作

    2.常用查找操作(R):
    User user = runner.query(sql,params,new BeanHandler(user.class)); 
    List list = runner.query(sql,params,new BeanListHandler(user.class));

    常用O-R Mapping映射工具
    Hibernate
    Ibatis
    Commons DbUtils(只是对JDBC简单封装)

    九、CachedRowSet(离线操作数据库)

    1.创建CachedRowSet
    CachedRowSetImpl cache = new CachedRowSetImpl();

    2.填充CachedRowSet
    //对查出的结果集提供分页功能
    cache.setPageSize(10);  //取10行数据
    cache.populate(rs,2);  //从第2行开始取,起始值是1

    3.更新CachedRowSet
    调用update***()方法后,调用updateRow方法
    更新至数据库,调用acceptChanges方法

  • 相关阅读:
    C# zip压缩文件的功能
    C#图解教程 第四章 第五章
    process.StandardOutput.ReadToEnd() 假死
    C# 图解教程 (类型 存储和变量)
    unity editor下选中GameObject粘贴复制pos信息
    Texture中指定具体颜色进行高亮显示
    unity 加载Assetbundle文件夹路径需要注意
    unity 文件移动注意 AB打包文件名注意小写
    linux总结应用之三 建立内核
    linux总结应用之二
  • 原文地址:https://www.cnblogs.com/hjl553155280/p/6170192.html
Copyright © 2011-2022 走看看