jdbc那点小事

1.Class.forName(),是根据类的名字将类装载到虚拟机里面了。把类装载到虚拟机里面和创建一个类的实例是不一样的,创建一个实例就会有一个
 实例产生,但是把类装载到虚拟机里面就不一定会有实例产生。
 
2.通过DriverManager.registerDriver()和System.setProperty()方式,会直接将驱动放入驱动列表里面。

3.通过Class.forName()方式,是将类加载到虚拟机里面,存在在虚拟机中的类的静态代码块会立即被虚拟机执行,所有的数据库开发商开发的驱动
 类Driver里面都有一段相同的代码(因为要遵循sun的标准),代码块如下:
 
public class Driver extends NonRegisterDriver implements java.sql.Driver{
 static {
  try{
   java.sql.DriverManager.registerDriver(new Driver());
  }catch(SQLException e){
   throw new RuntimeException("Can't register driver!");
  }
 }
 
 public Driver() throws SQLException{
  
 }
}

4.注册驱动是使用Class.forName()更好点,因为这样也可以注册一个驱动,而使用DriverManager.registerDriver()时,我们的参数是
 new 了一个驱动对象,但是数据库厂商提供的驱动类里面的静态代码块依然会执行,这样就相当于产生了两个驱动类实例,虽然对
 程序没有影响,但是Class.forName()形式的更好点。
 
5.如果我们的程序中没有导入数据库驱动jar包,那么DriverManager.registerDriver()形式就不能通过编译,因为我们 new 了一个对象
 作为参数,所以这样就依赖于数据库驱动 jar 包,即:无法导入Driver类,而且更换数据库时也要修改程序代码,而Class.forName()
 和System.setProperty()形式则可以编译,因为他们的参数都是字符串,没有依赖外部的数据库类,只有当程序运行时,实例化类时,
 才报异常。更换数据库时不需要更改代码,只要更改属性文件即可。
 
6.JDBC url是跟着数据库变动的,格式为: JDBC:子协议:子名称//主机名:端口号/数据库名?属性名=属性值&...
 mysql数据库的url: jdbc:mysql://localhost:3306/mydb 此url没有子名称,oracle就有,jdbc是数据库url的协议,类似万维网上的
 http或者文件上传中的ftp等。如果我们使用的主机名和端口号都是默认的,则可以不写,例如上面的url也可以写成jdbc:mysql:///mydb
 
7.数据库连接(Connection)是非常稀有的资源用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用
 原则是尽量晚创建,尽量早的释放。
 
8.当我们取得从数据库中获得的值时有两种方式的参数,一种是所以序列号,一种是字段列名,如果按照索引序列号则必须按照数据库中列的
 顺序获得值,如果是按照字段列名则与顺序无关,可以任意顺序取。
 
9.sql注入时,我们只要输入一个 ' or 1 or' 即可。例如:

  String name = "' or 1 or '";        //这两个单引号是要与sql语句中的前后两个单引号进行匹配的
  sql = "select * from t_user where name ='" + name + "'";
  
 组成的sql语句为:
  
  select * from t_user where name =''or 1 or''
  
10.使用PreparedStatement和Statement的区别:
 PreparedStatement会预处理sql语句,同时也可以屏蔽掉一下特殊字符,防止sql注入。其次,如果同一个sql执行次数比较多时PreparedStatement
 比Statement的效率高,如果同一个Sql执行的次数比较少则效率Statement比PreparedStatement高。
 
11.PreparedStatement(从Statement扩展而来)相对Statement的优点:
 1.没有SQL注入问题。
 2.Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
 3.数据库和驱动可以对PreparedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)
 
12.处理大文本(即:把一个文件【中的内容】存入数据库):
 String sql = "insert into clob_test(big_test) values(?)";
 ps = conn.prepareStatement(sql);
 File file = new File("src/cn/itcast/DBUtil3.java");
 Reader reader = new BufferedReader(new FileReader(file));
 ps.setCharacterStream(1,reader,(int)file.length());
 //ps.setString(1,x); //可以将文件内容构建成一个String后存到数据库中,String没有大小限制,但是数据库中要定义成clob
 int i = ps.executeUpdate();
 
13.获取数据库中的大文本类型:
 Clob clob = ResultSet.getClob(1);
 Reader reader = clob.getCharacterStream();
 //reader = ResultSet.getCharacterStream(1);
 //String s = rs.getString(1);
 File file = new File("DBUtil3_bak.java");
 Writer writer = new BufferedWriter(new FileWriter(file));
 char[] buff = new char[1024];
 for(int i = 0;(i = reader.read(buff)) > 0;){
  writer.writer(buff,0,i);
 }
 
14.处理二进制文件(例如:图片)
 String sql = "insert into blog_test(big_bit) values(?)";
 ps = conn.prepareStatement(sql);
 File file = new File("IMG_001.jpg");
 InputStream in = new BufferedInputStream(new FileInputStream(file));
 ps.setBinaryStream(1,in,(int)file.length());
 ps.executeUpdate();
 
15.读取Blob数据
 //Blob blob = rs.getBlob(1);
 //InputStream in = blob.getBinaryStream();
 InputStream in = rs.getBinaryStream(1);
 File file = new File("IMG_002.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);
 }
 
16.一旦Connection关闭(断开)了,那么ResultSet中的数据也随即消失。所以当我们访问数据库时得到的ResultSet需要及时
 处理,不能继续往上传递,除非我们不关闭Connection。即:在使用ResultSet之前不能关闭Connection连接。所以
 Connection一旦关闭(断开)Statement和ResultSet就无效了。
 
17.异常处理:
 从JDBC连接代码的那个文件中将异常往上抛,跑到调JDBC代码的DAO层时将异常捕获,这时捕获到的异常为SQL异常,我们不能
 再将这个SQL异常继续往上跑,那样会污染我们service层的代码,所以我们要在DAO层将这个SQL异常装换为运行时异常进行处
 理,代码如下:
  
 自定义异常类:
  public class DaoException extends RuntimeException {
  
   public DaoException() {
   
   }
  
   public DaoException(String message) {
    super(message);
   }
  
   public DaoException(Throwable cause) {
    super(cause);
   }
  
   public DaoException(String message, Throwable cause) {
    super(message, cause);
   }
 
  }
  
 捕捉异常的DAO层代码:
  public User findUser(String id) {
   try {
    String sql = "";
    conn = dbutil.getInstance().getConnection();
    state = conn.createStatement();
    rs = state.executeQuery(sql);
    while(rs.next()){
     u = new User();
    }
   } catch (SQLException e) {
    throw new DaoException(e.getMessage(),e);
   }finally{
    dbutil.free(rs, state, conn)
   }
   
   return u;
  }
  
18.动态切换DAO层代码
 使用工厂方法模式替换Dao层实现类:
 
 属性文件中:src/daoconfig.properties
 userDaoClass=com.java_min.DaoImpl.UserDaoImpl
 
 工厂类中:
 public class DaoFactory{
  
  private  UserDao userDao = null;
  private static DaoFactory instance = new DaoFactory();
  
  private DaoFactory(){
   Properties prop = new Properties();
   InputStream in = new FileInputStream(new File("src/daoconfig.properties"));
   prop.load(in);
   String userDaoClass = prop.getProperty("userDaoClass");
   userDao = (UserDao)Class.forName(userDaoClass).newInstance();
  }
  
  public static DaoFactory getInstence(){
   return instance;
  }
  
  public UserDao getUserDao(){
   return userDao;
  }
 }
 
 总结:工厂一般都是用单例的,其次我们用实现DAO层的动态切换就用使用读取属性文件的方式,读取
   实现类的全路径表示,后使用反射机制得到DAO层实现类的实例,此处读取属性文件的方式有
   两种多种方式,上面是使用了文件流对象读取属性文件,我们还可以使用“类加载器”读
   取属性文件,代码入:
    Properties prop = new Properties();
    InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("daoconfig.properties");
    prop.load(in);
    
   “类加载器”是每个类都有的,它可以加载“类.class”还可以加载其他东西,如上面的属性文件,文件流与类加载器加载
   属性文件的不同之处在于,文件流加载时,需要给出属性文件的路径,当使用类加载器加载时我们不需要给出属性文件
   的路径,只需要将属性文件放入classpath目录下即可,类加载器会从classpath目录下寻找的。
   
19.事物保存点处理:
 有事事务回滚时,我们我们并不想让所有的事务都回滚,而是回滚到某个点上。可以使用事务保存点。代码如下:
 
 Savepoint sp;
 
 try{
  String sql = "";           
  state.executeUpdate(sql);
  
  sp = conn.setSavepoint();      1.
  
  sql = "";
  state.executeUpdate(sql);
  
  sql = "";
  rs = state.executeQuery(sql);
  while(rs.next()){
   throw new RuntimeExcetion();
  }
 }catch(Exception){
  if(conn != null && sp != null){
   conn.rollback(sp);       //如果不带参数就会全部回滚,带了参数就会回滚到此参数指定的事务保存点上
   conn.commit();
  }
 }finally{
  if(conn != null){
   conn.rollback();
  }
 }
 
 以上就会回滚到1.出,只有第一条语句执行成功提交,后面的那个更新被回滚。
 
20.数据库隔离级别(假如有两个人A、B同时修改数据库,两个人操作数据库的时候就是个子开启个子的事务,两个事务互不干扰,即:隔离性):
 未提交读:假如A修改了数据库中的数据,但是还没有提交事务,这是B去读数据也能读到刚才A修改的数据,如果A此时回滚了事务,那么B读取的这个数据就是脏数据了。
 提交读:假如A修改了数据库中的数据,但是还没有提交事务,这是B去读数据是读不到A刚才修改的数据的,如果A此时提交了事务,那么B就可以读取这个数据了。
 可重复读:假如A读取了数据库总的数据,此时B在A读取了数据后修改了数据库中的数据,也提交了事务,那么A读取的数据是不会改变的,如果A再重新读取数据也是读取不到的,
    除非A从新开启一个事务就可以读取B修改后的数据了。
 序列化:就是相当于数据库加锁,如果A开启事务操作数据时(包括读取数据),那么B随后也开启事务操作数据,如果B提交事务时是不能提交事务的,那么事务会停下来等待A的
   事务提交,如果A不提交事务,B的操作就会一直处于等待状态,一旦A提交了事务,那么B的事务马上就会结束等待,执行事务提交。

21.JDBC调用存储过程:
 
 存储过程:
  DELIMITER $$
  
  DROP PROCEDURE IF EXISTS 'jdbc'.'addUser' $$
  CREATE PROCEDURE 'jdbc'.'addUser'(in pname varchar(45),in birthday date,in money float,out pid int)
  BEGIN
   insert into user(name,birthday,momey) values(pname,birthday,money);
   select last_insert_in() into pid;  //last_insert_in 这是数据库中的一个函数,用于查询当前线程插入的数据的索引号
  END $$
  DELIMITER ;
 
 java调用存储过程:
  String sql = "{call addUser(?,?,?,?)}";
  cs = conn.prepareCall(sql);
  cs.registerOutParameter(4,Types.INTEGER);
  cs.setString(1,"simier");
  cs.setDate(2,new java.sql.Date(System.currentTimeMillis()));
  cs.setFloat(3,100f);
  cs.executeUpdate();
  
  int id = cs.getInt(4);
  
22.批处理:
 PreparedStatement:
 
 String sql = "insert into t_user values(?,?)";
 ps = conn.prepareStatement(sql);
 for(int i = 0;i < 1000;i ++){
  ps.setString(1,"name" + i);
  ps.setFloat(2,100f + i);
  
  ps.addBatch();
 }
 
 int[] is = ps.executeBatch();

------------------------------------------
 
 Statement:
 
 for(int i = 0;i < 1000;i ++){
  String sql = "insert into t_user values(i,"name" + i)";
  state.addBatch(sql);
 }
 state.executeBatch();

23.可滚动结果集与分页技术:
 String sql = "select * from t_user where id < 5";
 while(rs.next()){
  System.out.println(rs.getString(0) + "----" + rs.getString(1));
 }
 
 System.out.println("*************");
 
 rs.absolute(4);  //将指针定位到结果集的第四条记录上
 
 if(rs.previous()){
  System.out.println(rs.getString(0) + "----" + rs.getString(1));
 }
 
 输出结果:
 
 1   name1
 2   name2
 3   name3
 4   name4
 5   name5
 
*********

 3   name3
 
 我们可以使用absolute()方法实现分页技术,比如在数据库中从第100条记录开始
 取10条记录(此分页属于内存分页,效率比较低,这种方式可以在数据库不支持数
 据库分页时使用这种方法)。
 
 rs.absolute(100);
 int i = 0;
 while(rs.next() && i < 10){
  System.out.println(rs.getString(0) + "----" + rs.getString(1));
 }
 
24.结果集更新:
 while(rs.next()){
  rs.updateFloat("money",300f);
  rs.updateRow();
 }
 
25.反射机制:
 1.构造具有相同参数签名的对象的通用类,代码如下:
 
 static Object create(Class clazz)throws Exception{  //动态传入一个参数,使用该参数创建一个对象
  Constructor con = clazz.getConstructor(String.class);
  Object obj = con.newInstance("simier");  //构造具有一个初始化参数的对象
  return obj;
 }
 
 2.使用反射机制调用对象的方法,代码如下:
 static void invoke(Object obj){
  Method[] ms = obj.getClass().getMethods();  //获得该类的所有方法,以及超类中的方法
  Method[] m = obj.getClass().getDeclaredMethods();  //获得自定义的所有方法,超类和私有的方法无法获得
  for(Method i:m){
   System.out.println(i);
  }
 }
 
 3.使用反射机制精确调用对象的方法,代码如下:
 static void invoke(Object obj){
  Method m = obj.getClass().getMethod(methodName,null); //methodName是想要调用的方法的名字,null表示该方法不需要传入参数
  m.invoke(obj,null);  //执行该方法,obj必须是实例化的对象,这个方法接受的参数都必须是对象
 }
 
 3.使用反射机制额调用对象的属性,代码如下:
 static void field(Class clazz){
  Field fs = clazz.getDeclaredFields();  //得到该类的所有自定义属性
  fs = clazz.getField();  //得到该类的所有public类型属性
 }
 
26.编写一个基本的连接池实现连接的复用,代码如下:

 package com.java_min.util;

 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 import java.util.LinkedList;
 
 //连接池类
 public class MyDataSource {
 
  //此段代码可以放到JDBCUtil类中,当创建连接池对象时,以参数的形式传进来
  /*private static String url = "jdbc:mysql://localhost:3306/jdbc";
  private static String userName = "root";
  private static String password = "root";
  private static String driver = "com.mysql.jdbc.Driver";*/
  
  private String url;
  private String userName;
  private String password;
  
  private static int initCount = 5;
  private static int maxCount = 10;
  private int currentCount = 0;
  
  private LinkedList<Connection> connectionPool = new LinkedList<Connection>();
  
  //为了灵活性,此段代码也放在JDBCUtil类中
  /*static {
   try {
    Class.forName(driver);
   } catch (ClassNotFoundException e) {
    new ExceptionInInitializerError(e);
   }
  }*/
  
  public MyDataSource(String userName,String password,String url){
   this.userName = userName;
   this.password = password;
   this.url = url;
   try {
    for(int i = 0;i < initCount;i++){
     this.connectionPool.addLast(this.createConnection());
     this.currentCount ++;
    }
   } catch (Exception e) {
    throw new ExceptionInInitializerError(e);
   }
  }
  
  public Connection getConnection() throws SQLException{
   synchronized(connectionPool){
    if(this.connectionPool.size() > 0){
     return this.connectionPool.removeFirst();
    }
    if(this.currentCount < maxCount){
     this.currentCount ++;
     return this.createConnection();
    }
    throw new SQLException("已没有连接");
   }
  }
  
  public void free(Connection conn){
   this.connectionPool.addLast(conn);
  }
  
  private Connection createConnection() throws SQLException{
   return DriverManager.getConnection(url,userName,password);
  }
  
  //此段代码放在JDBCUtil类中
  /*public static void free(ResultSet rs,Statement state,Connection conn){
   try {
    if(rs != null){
     rs.close();
    }
   } catch (SQLException e) {
    System.out.println(e.getMessage());
    e.printStackTrace();
   }finally{
    try {
     if(state != null){
      state.close();
     }
    } catch (SQLException e) {
     System.out.println(e.getMessage());
     e.printStackTrace();
    }finally{
     try {
      if(conn != null){
       myDataSource.free(conn);
      }
     } catch (SQLException e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
     }
    }
   }
  }*/
 }
 
27.开源DBCP数据库连接池:
 需要导入的jar文件:
  commons-collections-3-1.jar
  commons-dbcp-1.2.2.jar
  commons-pool.jar
  
 创建数据源:
  Properties ps = new Properties();
  InputStream is = DBUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
  ps.load(is);
  DataSource ds = BasicDataSourceFactory.createDataSource(ps);
  
28.将DAO中的修改方法提取到抽象父类中,具体代码如下:
 
 抽象父类:
 public abstract class AbstractDao{
  public int update(String sql,Object[] args){
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try{
    conn = DBUtils.getConnction();
    ps = conn.prepareStatement(sql);
    for(int i = 0;i < args.length;i ++){
     ps.setObject(i + 1,args[i]);
    }
    return ps.executeUpdate();
   }catch(Exception e){
    e.printStack();
   }finally{
    DBUtils.free(rs,ps,conn);
   }
  }
 }
 
 继承抽象父类的子类:
 public class UserDaoImpl extends AbstractDao{
  public void update(User user){
   String sql = "update t_user set name=?,birthday=?";
   Object[] args = new Object[]{user.getName(),user.getBirthday()};
   int i = super.update(sql,args);
  }
 }
 
29.使用模板方法设计模式处理DAO中的查询方法,代码如下:
 
 模板类:
 public abstract class AbstractDao{
  public Object find(String sql,Object[] arsg){
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try{
    conn = DBUtils.getConnection()
    ps = conn.prepareStatement(sql);
    for(int i = 0;i < args.length;i ++){
     ps.setObject(i + 1,args[i]);
    }
    rs = ps.executeQuery();
    Object obj = null;
    while(rs.next()){
     obj = rowMapper(rs);
    }
   }catch(Exception e){
    e.printStack();
   }finally{
    DBUtils.free(rs,ps,conn);
   }
  }
  
  protected abstract Object rowMapper(ResultSet rs);
 }
 
 子类代码:
 public class UserDaoImpl extends AbstractDao{
  public User getUser(String loginName,String password){
   String sql = "select id,name,money from t_user where name=?";
   Object[] args = new Object[]{loginName};
   Object user = super.find(sql,args);
   return (User)user;
  }
  
  protected Object rowMapper(ResultSet rs){
   User user = new User();
   user.setId(rs.getInt("id"));
   user.setName(rs.getString("name"));
   user.setMoney(rs.getFloat("money"));
   
   return user;
  }
 }
 
 
30.使用策略模式对模板方法设计模式进行改进,代码如下:

 接口类:
 public interface RowMapper{
  public Object mapRow(ResultSet rs)throws SQLException;
 }

 模板类:
 public class DaoTemplete{
  public Object find(String sql,Object[] arsg,RowMapper rowMapper){
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   try{
    conn = DBUtils.getConnection()
    ps = conn.prepareStatement(sql);
    for(int i = 0;i < args.length;i ++){
     ps.setObject(i + 1,args[i]);
    }
    rs = ps.executeQuery();
    Object obj = null;
    while(rs.next()){
     obj = rowMapper.mapRow(rs);
    }
   }catch(Exception e){
    e.printStack();
   }finally{
    DBUtils.free(rs,ps,conn);
   }
  }
 } 
 
 子类:
 public class UserDaoImpl{
  
  DaoTemplete template = new DaoTemplete();
  
  public User findUser(String loginName,String password){
   String sql = "select id,name,money,birthday from t_user where name=?";
   Object[] args = new Object[]{loginName};
   Object user = this.template.find(sql,args,new RowMapper(){      1.
    public Object mapRow(ResultSet rs)throws SQLException{
     return rs.getString("name");
    }});
    return ((User)user).getName();
  }
 }
 
 上面1.处的匿名内部类也可以使用一个实现了RowMapper接口的实现类的实例代替。
 
31.Spring框架中提供了一个JdbcTemplate工具类,JdbcTemplate类对JDBC API进行了很好的封装这个类就像我们自己
 对JDBC进行封装一样,只是代码更健壮和功能更强大而已,我们以后在实际项目中可以使用JdbcTemplate类来完全
 替代直接使用JDBC API,这与直接使用JDBC API没太大的性能区别,使用JdbcTemplate类需要额外从Spring开发包
 中导入spring.jar和commons-logging.jar包。
 
 org.springframework.jdbc.core.JdbcTemplate对象介绍:
 
 构造JdbcTemplate对象时要给其传一个数据源类型的参数,有两种方式,构造对象时传入一个参数,或者构造完后使
 用对象的setter方法将数据源设置进去。代码如下:
 
 1. JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
  
 2. JdbcTemplate jt = new JdbcTemplate();
  jt.setDataSource(dataSource);
  
 JdbcTemplate对象API介绍:
 
 1. jt.queryForObject(sql,args,rowMapper);
 
 sql:为我们传入的预sql语句
 args:为一个Object类型的数组,为填充我们传入的预sql语句的占位符
 rowMapper:为一个行映射器接口
 
 示例代码如下:
 
  String sql = "select id,name,money from t_user where name=?";
  Object[] args = new Object[]{name};
  Object user = jt.queryForObject(sql,args,new RowMapper(){
    
   public Object mapRow(ResultSet rs,int rowNum)throws SQLException{  //匿名内部类,回调函数
    User user = new User();
    user.setId(rs.getInt("id"));
    user.setName(rs.getString("name"));
    user.setMoney(rs.getFloat("money"));
    return user;
   }
  });
  return (User)user;

 2.  Object user = jt.queryForObject(sql,args,argTypes,new BeanPropertyRowMapper(Class));  //只能返回一个结果,如果有多个结果该方法就会报异常
  
  sql:我们传入的预处理sql语句
  args:为一个Object类型的数组,用来填充我们预处理sql的占位符的
  argTypes:用来定义传入的参数的类型,此参数可选,如果不选spring会根据反射机制获得每个传入的参数的类型
  BeanPropertyRowMapper:此类实现了RowMapper接口,可以用来代替上面那个回调方法。但是使用此对象的时候
        有一些限制条件,构造BeanPropertyRowMapper对象时必须要传入要查询对象的类,
        之后BeanPropertyRowMapper对象会使用反射机制将查询出来的结果赋值到对应的对象
        属性上去,所以此处条件为,java类中的属性必须于数据库中的字段同名,或者命名单
        词相同,彼此都符合java和数据可的规范,如果java类中的属性命名与数据库中的字段
        名不相同,那么可以在sql语句中使用别名的方式使之与java类中的属性同名。
        
  示例代码如下:
  
  JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
  String sql = "select id,user_name,money,user_birthday_date as birthday from t_user where name=?";
  Object[] args = new Object[]{name};
  //int[] argTypes = new int[]{Types.INTEGER};
  //Object user = jt.queryForObject(sql,args,argTypes,new BeanPropertyRowMapper(User.class));  //加不加输入参数的类型定义,效果一样,不加Spring也会通过反射机制获得参数类型的
  Object user = jt.queryForObject(sql,args,new BeanPropertyRowMapper(User.class));
  return (User)user;
  
  java代码:
  private String id;
  private String userName;
  private float money;
  private Date birthday;
  
  总结,java类里面的属性一定要大于或者等于sql语句中的属性的数量。
  
 3. List users = jt.query(sql,args,new BeanPropertyRowMapper(User.class));  //可以返回多个结构
 
  sql:我们传入的预处理sql语句
  args:为一个Object类型的数组,用来填充我们预处理sql的占位符的
  BeanPropertyRowMapper:此类实现了RowMapper接口,可以用来代替上面那个回调方法。但是使用此对象的时候
        有一些限制条件,构造BeanPropertyRowMapper对象时必须要传入要查询对象的类,
        之后BeanPropertyRowMapper对象会使用反射机制将查询出来的结果赋值到对应的对象
        属性上去,所以此处条件为,java类中的属性必须于数据库中的字段同名,或者命名单
        词相同,彼此都符合java和数据可的规范,如果java类中的属性命名与数据库中的字段
        名不相同,那么可以在sql语句中使用别名的方式使之与java类中的属性同名。
        
  示例代码如下:
  
  JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
  String sql = "select id,user_name,money,user_birthday_date as birthday from t_user where id<?";
  Object[] args = new Object[]{id};
  List users = jt.query(sql,args,new BeanPropertyRowMapper(User.class));
  return users;
  
  java代码:
  private String id;
  private String userName;
  private float money;
  private Date birthday;
  
  总结:JdbcTemplate对象的query方法可以将查询出的结构组装成我们所给的匹配对象,后放入到List对象中,返回给我们
  
 4. String sql = "select count(*) from t_user";
  int n = jt.queryForInt(sql);
  
 5. String sql = "select name from t_user where id=" + id;
  Object name = jt.queryForObject(sql,String.class);  //String.class 参数代表要返回的结果的期望类型,不给也可以,给了后该方法就会对结果进行校验,不会就不会校验
  return (String)name;
  
 6. String sql = "select id as userId,name,money,birthday from user where id=" +id;
  Map map = jt.queryForMap(sql);
  
  map对象中的内容为:{userId=1,name=simier,money=99999900.0,birthday=1985-06-01}该对象中的key名就为
  我们sql语句中的字段名,注意那个id的别名,value值为数据库中对应字段的值。
  
 7.  String sql = "select id as userId,name,money from t_user where id=" +id;
  jt.queryForList(sql);
  
  此方法将返回一个List对象,其将查询出来的数据先放入一个Map后将Map放入List中,即:List中为一个个Map对象,而一个Map中存放了一条记录
  
 8. jt.execute(new ConnectionCallback(){
 
  public Object doInConnection(Connection conn)throws SQLException,DataAccessException{
   String sql = "";
   PreparedStatement ps = conn.prepareStatement(sql,Statement,RETURN_GENERATED_KEYS);
   ps.setString(1,user.getName());
   ...
   ps.executeUpdate();
   
   ResultSet rs = ps.getGeneratedKeys();
   if(rs.next()){
    user.setId(rs.getInt(1));
   }
   
   return user;
  }});

32.使用支持命名参数的JdbcTemplate对象NamedParameterJdbcTemplate,代码如下:

 NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource); 
 String sql = "select id,name,money,birthday from t_user where name=:names and money > :m and id < :id";  //命名参数名定义随意
 
 Map params = new HashMap();
 params.put("names",user.getName());
 params.put("m",user.getMoney());
 params.put("id",user.getId());
 
 Object u = named.queryForObject(sql,params,new BeanPropertyRowMapper(User.class));  //仅适合查找一个对象,少于一个或多于一个都会抛异常,命名参数为一个Map对象
 
 以上代码也可以换成一下形式,下面代码更具有对象性,代码如下:
 
 NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource); 
 String sql = "select id,name,money,birthday from t_user where name=:name and money > :money and id < :id";  //命名参数名定义必须与JavaBean中的属性同名
 SqlParameterSource ps = new BeanPropertySqlParameterSource(user);           //user参数是一个User对象,Spring会根据反射到JavaBean中寻找与SQL语句中命名参数同名的属性,获取其值赋给SQL占位符
 Object u = named.queryForObject(sql,params,new BeanPropertyRowMapper(User.class));  //仅适合查找一个对象,少于一个或多于一个都会抛异常,命名参数为一个BeanPropertySqlParameterSource对象

 ---------------------
 
 NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource);
 String sql = "insert into t_user(name,birthday,money) values(:name,:birthday,:money)"; //此处不能使用占位符?必须使用与JavaBean中属性同名的命名参数
 SqlParameterSource ps = new BeanPropertySqlParameterSource(user); //此处参数user为User对象
 KeyHolder keyHolder = new GeneratedKeyHolder();
 named.update(sql,ps,keyHolder);  //此方法更新(插入)结果后,会返回所更新(插入)的记录的主键值,并将其放入keyHolder对象中
 int id = keyHolder.getKey().intValue();  //如果是双主键可以使用方法getKeys(),该方法会返回一个Map对象,Map的key的值是表中字段的名字,value是表中字段的值
 return id;

33.使用SimpleJdbcTemplate和泛型技术简化代码(JDK1.5),代码如下:
 public class SimpleJdbcTemplateTest{
  static SimpleJdbcTemplate simple = new SimpleJdbcTemplate(org.apache.commons.BasicDataSource);
  
  static User find(String name){
   String sql = "select id,name,money,birthday from t_user where name=? and money=?";
   User user = simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(User.class),name,100f); //此方法返回的类型有newInstance的参数决定
   return user;
  }
  
  //为了更大的灵活性,以上查询方法可用以下代码(泛型)替换
  
  static <T>T find(String name,Class<T> clazz){
   String sql = "select id,name,money,birthday from t_user where name=? and money=?";
   Object obj = simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(clazz),name,100f); //此方法返回的类型有newInstance的参数决定
   return obj;
  }
 }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值