JDBC知识

1、JDBC所处位置:应用程序->JDBC API->数据库驱动->数据库
2、连接数据库的步骤:
   1>注册驱动(只做一次),有如下三种方式:
     //直接注册驱动,缺点:装载两次,且需要引入java.sql.DriverManager类,离开此驱动无法编译;
     DriverManager.registerDriver(new com.mysql.jdbc.Driver());
     //drivers是一个vector集合,直接从中获取属性,不会对具体的驱动类产生依赖,但注册不太方便
     System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver:driver1:driver2");
     //mysql的Driver类的静态代码块中已经注册,java.sql.DriverManager.registerDriver(new Driver());直接调用即可;优点:不会对具体的驱动类产生依赖,便于配置,无需修改代码,推荐方式;
     Class.forName("com.mysql.jdbc.Driver");
   2>建立连接(Connection)
     Connection conn = DriverManager.getConnection(url,user,password);
     url格式:
     jdbc:子协议:子名称//主机名:端口/数据库名?属性名=属性值&...
     如:jdbc:mysql://localhost:3306/jdbc,默认主机名:端口为localhost:3306,可省略;
     user,password可以用“属性名=属性值”方式告诉数据库;
     其他参数如:useUnicode=true&characterEncoding=GBK;
   3>创建执行SQL的语句(Statement)
     Statement st = conn.createStatement();
   4>执行语句
     ResultSet rs = st.executeQuery("select * from user");
   5>处理执行结果(ResultSet)
     while(rs.next())
     {
         System.out.println(rs.getObject(1)+"\t"+rs.getObject(2));
     }
   6>释放资源,非常重要,与创建顺序相反,数据库连接connection是非常稀缺的资源,消耗cpu,占内存很大,connection使用原则是尽量晚创建,尽量早关闭;
     rs.close();
     st.close();
     conn.close();
    
3、规范的数据库连接方法:
  static void template()
  {
   Connection conn = null;
   Statement st = null;
   ResultSet = null; //import java.sql.ResultSet,java标准库中的类;
   try
   {
    // 1.注册驱动,工具类中实现
    // 2.建立连接
    conn = JdbcUtils.getConnection();
    // 单例模式:
    // conn = JdbcUtils.getInstance().getConnection();
   
    // 3.创建语句
    st = conn.createStatement();
   
    // 4.执行语句
    ResuleSet rs = st.executeQuery("select * from user");
   
    // 5.处理结果
    while(rs.next())
       {
         System.out.println(rs.getObject(1)+"\t"+rs.getObject(2));
       }
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, st, conn);
   }
  }
 
  //连接数据库工具类
  public final class JdbcUtils
  {
   // 私有变量,初始化连接参数
   private static String url = "jdbc:mysql://localhost:3306/jdbc";
   private static String user = "root";
   private static String password = "";
  
   // 私有构造器
   private JdbcUtils()
   {
   
   }
  
   // 单例模式
   /* private static JdbcUtils instance = new JdbcUtils();
    public static JdbcUtils getInstance()
    {
     // 延迟加载
    if(instance == null)
    {
     // 加锁解决并发问题,将类锁起再构造
     synchronized(JdbcUtils.class)
     {
      // 双重检查,不可少
      if(instance == null)
      {
       instance = new JdbcUtils();
      }
     }
    }
     return instance;
    }*/
  
   // 静态代码块,注册驱动
   static
   {
    try
    {
     Class.forName("com.mysql.jdbc.Driver");
    }
    catch(ClassNotFoundException e)
    {
     throw new ExceptionInInitializerError(e);
    }
   }
  
   // 静态方法,创建连接
   public static Connection getConnection() throws SQLException
   {
    return DriverManager.getConnection(url, user,password);
   }
  
   // 静态方法,释放资源
   public static void void free(ResulteSet rs, Statement st, Connection conn)
   {
    try
    {
     if(rs != null)
     {
      rs.close();
     }
    }
    catch(SQLException e)
    {
     e.printStackTrace();
    }
    finally
    {
     try
     {
      if(st != null)
      {
       st.close(); 
      }
     }
     catch(SQLException e)
     {
      e.printStackTrace();
     }
     finally
     {
      try
      {
       if(conn != null)
       {
        conn.close();
       }
      }
      catch(SQLException e)
      {
       e.printStackTrace();
      }
     }
    }
   }
  }
 
4、增删改查操作(CRUD):
  增删改用Statement.executeUpdate来完成,返回整数(匹配的记录数),这类操作相对简单;
  查询用Statement.executeQuery来完成,返回的是ResultSet对象,ResultSet中包含了查询的结果;
  //查询操作
  static void read() throws SQLException
  {
   Connection conn = null;
   Statement st = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    st = conn.createStatement();
   
    // 4.执行语句
    ResuleSet rs = st.executeQuery("select id,name,birthday,money from user");
   
    // 5.处理结果
    while(rs.next())
       {
         System.out.println(rs.getObject("id") + "\t"
         + rs.getObject("name") + "\t"
         + rs.getObject("birthday") + "\t"
         + rs.getObject("money"));
       }
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, st, conn);
   }
  }
 
  //增加操作
  static viod create throws SQLException
  {
   Connection conn = null;
   Statement st = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    st = conn.createStatement();
   
    // 4.执行语句
    String sql = "insert into user(name,birthday,money) values('name1','1990-01-01','1000'), from user";
    // 返回int值,代表有几行记录受影响
    int i = st.executeUpdate(sql);
   
    // 5.处理结果
    System.out.println("i=" + i);
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, st, conn);
   }
  }
 
  // 修改操作
  static viod update throws SQLException
  {
  Connection conn = null;
  Statement st = null;
  ResultSet rs = null;
  try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    st = conn.createStatement();
   
    // 4.执行语句
    String sql = "update user set money=money+10";
    // 返回int值,代表有几行记录受影响
    int i = st.executeUpdate(sql);
   
    // 5.处理结果
    System.out.println("i=" + i);
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, st, conn);
   }
  }
 
  // 删除操作
  static viod delete throws SQLException
  {
   Connection conn = null;
   Statement st = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    st = conn.createStatement();
   
    // 4.执行语句
    String sql = "delete from user where id>4";
    // 返回int值,代表有几行记录受影响
    int i = st.executeUpdate(sql);
   
    // 5.处理结果
    System.out.println("i=" + i);
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, st, conn);
   }
  }
 
4、SQL注入问题:
  在SQL中包含特殊字符或SQL的关键字(如' or 1 or ')时Statement将出现不可预料的结果,如出现异常或查询结果不正确,可用PreparedStatement来解决。
   public class SQLInject
  {
    static void read(String name) throws SQLException
    {
     Connection conn = null;
    Statement st = null;
    ResultSet rs = null;
    try
    {
      // 2.建立连接
     conn = JdbcUtils.getConnection();
   
     // 3.创建语句
     st = conn.createStatement();
   
     String sql = "select * from user where name= '" + name +"'";
   
     // 4.执行语句
     ResuleSet rs = st.executeQuery(sql);
   
     System.out.println("sql:" + sql);
    }
    finally
    {
     // 6.释放资源
     JdbcUtils.free(rs, st, conn);
    }
   }
 
   public static void main(String[] args) throws SQLException
   {
    read("' or 1 or '");
   }
  }
  输出结果:sql:select * from user where name='' or 1 or '',并输出所有user表记录。
  处理方法:Statement换成PreparedStatement预处理。
  static void read(String name) throws SQLException
  {
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    String sql = "select * from user where name=?";
    ps = conn.createPreparedStatement(sql);
    ps.setString(1, name);
   
    // 4.执行语句
    ResuleSet rs = ps.executeQuery();
    //ResuleSet rs = ps.executeQuery(sql);调用Statement类的executeQuery,这里会直接执行语句,不会替换问号,导致报错。
   
    System.out.println("sql:" + sql);
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, ps, conn);
   }
  }
  PreparedStatement(继承自Statement)相对Statement的优点:
  1>没有SQL注入问题;
  2>Statement会使数据库频繁编译SQL,可能造成缓冲区溢出;
  3>数据库和驱动可以对PreparedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效);
  4>执行多条语句时,速度更快。
 
5、jdbc中的数据类型和日期问题:
  jdbc.date类继承自util.date类
   1>util.date转化为jdbc.date:
  ps.setDate(new java.sql.Date(birthday.getTime()));
  2>jdbc.date转化为util.date:
  birthday = rs.getDate("birthday"); //输出:2010-12-06,jdbc.Date类中已经做了格式化操作;
  birthday = new java.util.Date(rs.getDate("birthday").getTime()); //输出:Sat Dec 06 00:00:00 CST 2010
 
6、jdbc访问大段文本数据(clob):
  varchar类型缺省45个字符,最大长度255个字符。超过255需要用大文本类型存储。
  1>插入操作:
   static void create() throws SQLException,IOException
  {
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    String sql = "insert into clob_test(big_bit) values(?)";
    ps = conn.createPreparedStatement(sql);
    File file = new File("src/aa.text");
    Reader reader = new BufferedReader(new FileReader(file));
    ps.setCharacterStream(1, reader, (int)file.length);
   
    // 4.执行语句
    int i = ps.executeUpdate();
    reader.close();
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, ps, conn);
   }
  }
  2>读取操作:
   static void create() throws SQLException, IOException
  {
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    String sql = "select big_text from clob_test";
    ps = conn.createPreparedStatement(sql);
   
    // 4.执行语句
    int i = ps.executeQuery();
   
    // 5.处理结果
    while(rs.next())
    {
     Clob clob = rs.getClob(1);
     Reader reader = clob.getCharacterStream();
     //Reader reader = rs.getCharacterStream(1);
     File file = new File("bb.text");
     Writer writer = new BufferedWriter(new FileWriter(file));
     char[] buff = new char[1024];
     for(int i = 0; (i = reader.read(buff))>0;)
     {
      writer.write(buff, 0, i);
     }
     writer.close();
     reader.close();
    }
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, ps, conn);
   }
  }
 
7、jdbc访问二进制类型的数据(blob,长度64kb):
  1>插入操作:
  static void create() throws SQLException
  {
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    String sql = "insert into blob_test(big_bit) values(?)";
    ps = conn.createPreparedStatement(sql);
    File file = new File("src/aa.jpg");
    InputStream in = new BufferedInputStream(new FileInputStream(file));
    ps.setBinaryStream(1,in,(int)file.length);
   
    // 4.执行语句
    int i = ps.executeUpdate();
    in.close();
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, ps, conn);
   }
  }
  2>读取操作:
  static void create() throws SQLException
  {
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try
   {
    // 2.建立连接
    conn = JdbcUtils.getConnection();
   
    // 3.创建语句
    String sql = "select big_bit from blob_test";
    ps = conn.createPreparedStatement(sql);
   
    // 4.执行语句
    int i = ps.executeQuery();
   
    // 5.处理结果
    while(rs.next())
    {
     InputStream in = rs.getBinaryStream(1);
     File file= new File("aa_bak.jpg");
     OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
     byte[] buff = new byte[1024];
     for(int i = 0; (i = in.read(buff))>0;)
     {
      out.write(buff, 0, i);
     }
     out.close();
      in.close();
    }
   }
   finally
   {
    // 6.释放资源
    JdbcUtils.free(rs, ps, conn);
   }
  }

8、数据类型:
  几种特殊且比较常用的类型:
  DATA,TIME,TIEMSTAMP---date,time,datetime
  CLOB---text
   存:ps.setCharacterStream(index,reader,length);
       ps.setString(i,s);  //i指第几个参数
   取:reader = rs.getCharacterStream(i);
      reader = rs.getClob(i).getCharacterStream();
      String = rs.getString(i);  //i指第几列
  BLOB---blob
   存:ps.setBinaryStream(i,inputStream,length);
   取:rs.getBinaryStream(i);
      rs.getBlob(i).getBinaryStream();
     
9、异常处理

10、工厂模式

11、事务
   四大特性(ACID):
   原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中一部分;
   一致性(consistentcy):在事务处理前后,数据是一致的(数据库的数据完整性约束);
   隔离性(isolcation):一个事务对另一个事务的影响;
   持续性(durability):事务处理的效果能够被永久的保存下来;
   connection.setAutoCommit(false); //启动事务
   connection.commit(); //提交事务
   connection.rollback(); //回滚事务
   当只想撤销事务中的部分操作时,可使用SavePoint 
   SavePoint sp = connection.setSavePoint(); //设置保存点
   connection.rollback(savePoint); //回滚到保存点
   connection.commit();
   JTA:
   跨越多个数据源的事务,使用JTA容器实现事务;
   分成两个阶段提交:
   javax。transaction.UserTransaction tx = (UserTransaction)ctx.lookup("jdniName");
   tx.begin();
   //connection1,connection2可能来自不同的数据库
   tx.commit();或tx.rollback;
  
12、隔离级别
   作用:保证多线程并发读取数据时的正确性。
   java中设置隔离级别:
   connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
   一般无需自己设置隔离级别,因为每个数据的隔离基本实现都不一样,设置之后也不能保证得到自己想要的结果。
   三种错误:
   脏读:一个事务读取了另一个未提交的并行事务写的数据;
   不可重复读:一个事务读取前面已经读取过的数据,发现数据已被另一个已提交的事务修改过;
   幻读:一个事务再次读取数据,能够读取到另一个已提交事务增加的数据。
  
   隔离级别正确性表:
   √ 可能出现(每个数据库实现不一样,可能会有些许差别);× 不会出现;
   隔离级别          脏读 不可重复读 幻读
  读未提交(Read uncommitted) √   √      √
  读已提交(Read committed)  ×   √      √
  可重复读(Repeatable read)  ×   ×      √
  可串行化(Serializable)   ×   ×      ×
  
  mysql查询数据库隔离级别:
  select @@tx_isolation;
  mysql设置数据库隔离级别:
  set transaction isolation level read uncommitted;
  mysql打开事务:
  start transaction;
  
11、存储过程
  CallableStatement(从PreparedStatement扩展而来)
  使用方法如下:
  CallableStatement cs = connection.prepareCall("{call psname(?,?,?)}");
  cs.registerOutParameter(index,Types.INTEGER);
  cs.setXXX(i,xxx);
  cs.executeUpdate();
  int id = cs.getInt(index);
  
12、其他几个API
  获取主键:
  PreparedStatement ps = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
  ps.executeUpdate();
  ResultSet rs = ps.getGeneratedKey();
  int id = rs.getInt(1);
  批处理,可以大幅度提升大量增删改的速度:
  PreparedStatement.addBatch();
  PreparedStatement.executeBatch();
  可滚动的结果集:
  Statement st = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
  ResultSet rs = st.executeQuery(sql);
  rs.beforeFirst();
  rs.afterLast();
  rs.first();
  rs.isFirst();
  rs.last();
  rs.isLast();
  rs.absolute(9);
  rs.moveToInsertRow();
  可更新的结果集:
  Statement st = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
  rs.updateString("col name","new value");
  rs.updateRow();
  自动更新数据库记录后,读取的为更新后的新数据;此功能一般少用,将其查询与修改混合在一起操作,不利于模块化。
  
13、DatabaseMetaData和parameterMetaData
  DatabaseMetaData:
  DatabaseMetaData dbmd = connection.getMetaData();
  通过DatabaseMetaData可以获得数据库的相关信息:数据库名称,版本,厂商,是否支持事务,是否支持某种事务隔离级别,是否支持动态结果集等;
  ParameterMetaData:
  ParameterMetaData pmd = preparedStatement.getParameterMetaData();
  通过ParameterMetaData可以获得参数信息。
  ResultSetMetaData:
  ResultSetMetaData resultSetMetaDate = rs.getMetaData();
  通过ResultSetMetaData可以获取结果集的信息,包括结果集的列名、列类型、列值等。

14、数据源和连接池
   DataSource用来取代DriveManager来获取Connection;
   一般DataSource会用一个连接池来缓存,可以大幅度提高数据库的访问速度;
   通过Datasource获得的Connection都是已经被包裹的连接,不是原来驱动的连接,它的close方法已经被修改;
   连接池可以理解为一个能够存放Connection的Collection;
   我们的程序只和DataSource打交道,不会直接访问连接池;
  
   使用dpcp的步骤:
   导入三个包:
   commons-dpcp.jar,commons-pool.jar,commons-collections.jar;
   配置文件:
   dbcp.properties,配置一些指定属性;
   初始化数据源:
   DataSource myDataSource = BasicDataSourceFactory.createDataSource(prop);
   创建连接:
   Connection connection = myDatasource.getConnection();
  
15、使用继承优化JDBC代码(模板模式)
   优化查询语句,区分可变和不变的部分,sql和ResultSet的处理是变化部分,创建和释放资源是不变部分;
   提取超类,将不变部分放入超类,变化部分留给子类实现;
  
16、Spring和JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcTemplate
   查询带有参数和行映射的方法:
   public Object queryForObject(String sql, Object[] args, RowMapper rowMapper),使用自定义的RowMapper完成映射;
   一个RowMapper的常用实现BeanPropertyRowMapper,该实现可将结果集转换成一个Java Bean(字段名与Java Bean属性名不符合规范,可用别名处理)。
   public List query(String sql, Object[] args, RowMapper rowMapper),返回多个结果。
   public int queryForInt(String sql),如:select count(*) from user,其他结果比如String可用queryForObject方法向下转型。
   public Map queryForMap(String sql, Object[] args),返回Map类型。
   public List queryForList(String sql, Object[] args)返回Map集合。
   更新:
   public int update(String sql, Object[] args)
   插入数据并获得结果集:
   public Object execute(ConnectionCallBack action)
  
   JdbcTemplate中方法主要传递sql,和数组参数,其方法要求sql占位符和参数数组位置需要对应。
   描述:1、在excute方法中可以传递一个ConnectionCallback回调接口,在接口方法中将获取connection对象,可以自定义进行操作。
     2、结果集的封装可以使用spring的RowMapper接口对象,也可以使用rowBeanPropertyRowMapper,这个只需传递一个对象的class即可。

  NamedParameterJdbcTemplate是对JdbcTemplate进行了封装,主要多了一层对参数的解析,sql使用特殊组合的占位符,参数主要使用map,这样sql的占位符和参数数据就不需要在顺序上一一进行对应。
  关键点描述:SqlParameterSource ps = new BeanPropertySqlParameterSource(user);可以使用SqlParameterSource来传递一个对象,来对sql的占位符进行填值,
  KeyHolder keyHolder = new GeneratedKeyHolder();来捕获生成的主键值。

  SimpleJdbcTemplate的使用建立在JDK1.5版本之上,里面封装了一个NamedParameterJdbcTemplate,主要添加了支持变长参数。
  需要调用NamedParameterJdbcTemplate中有而SimpleJdbcTemplate中没有的方法时,可以使用simple.getNamedParameterJdbcOperations()。
  获取JdbcTemplate中方法simple.getJdbcOperations()。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值