jdbc那点小事

SQL Server连接器选型指南:DBeaver用JDBC还是jTDS驱动?实测对比与性能调优 本文深度对比了在DBeaver中连接SQL Server时,Microsoft JDBC驱动与开源jTDS驱动的性能差异与适用场景。通过实测数据,分析了两种驱动在连接成功率、大数据量查询、版本兼容性及企业级配置方面的表现,为数据库管理员和开发者的驱动选型与性能调优提供实用指南。 阅读详情

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;
  }
 }

PostgreSQL JDBC驱动高危漏洞CVE-2024-1597解析与修复指南 SQL注入是数据库安全领域长期存在的核心威胁,其原理在于攻击者通过输入恶意数据,篡改原始SQL语句的逻辑结构,从而执行非预期的数据库操作。在Java生态中,数据库驱动作为连接应用与数据库的底层桥梁,其安全性直接关系到整个系统的数据防护体系。CVE-2024-1597正是暴露于PostgreSQL JDBC驱动层的一个高危SQL注入漏洞,其技术价值在于警示开发者,即便应用层代码严格遵守了参数化查询等最佳实践,底层依赖组件的缺陷仍可能构成致命短板。该漏洞在特定的“简单查询模式”下,通过对参数占位符前负号的解析歧 阅读详情

相关推荐

达梦数据库Decimal主键精度丢失:JDBC驱动与JSON序列化全链路解决方案

在数据库应用开发中,高精度数值类型(如Decimal/Numeric)常用于存储超长整型主键ID,以确保数据的唯一性和精确性。其原理是通过固定精度和小数位数来准确表示大整数,避免浮点数类型的精度损失。这类高精度类型在金融、电商订单等需要精确计算的场景中具有重要技术价值。然而,在数据从数据库到前端的完整流转链路中,JDBC驱动版本缺陷和JSON序列化库的默认处理机制可能导致精度丢失,尤其是在结合特定框架(如若依)时。本文聚焦于达梦数据库环境下,针对“驱动版本”稳定性和“JSON序列化”配置两大核心热词,提供从

weixin_33404102的博客 338

JDBC:Java数组和数据库中Array类型的映射

如果使用Hibernate框架,Java类型和数据库类型的映射可以通过配置文件进行。 如果使用JDBC,那就必须自己弄明白映射的过程了。 其实过程也很简单: JDBC给我们提供了一个java.sql.Array类,我们可以使用java.sql.Connection对象创建Array类,来完成Java数组和Array类的映射。 比如我的数据表中有一个formats的字段,存储格式是Array

给永远比拿愉快 1万+

SQL Server 2005 JDBC驱动包下载与Java数据库连接实战

别再拍脑袋决定了,我们来做个决策树:graph TDA[新项目 or 关键系统?] -->|是| B[选 Microsoft JDBC Driver]A -->|否| C[jTDS 是否已在稳定运行?C -->|是| D[可维持现状,但规划替换]C -->|否| E[临时测试用 jTDS 快速接入]B --> F[推荐 v3.0 + sqljdbc4.jar]E --> G[仅限非敏感场景]最终建议:- ✅新建项目、金融/政务系统 → Microsoft JDBC Driver。

weixin_35755640的博客 1150

JDBC给存储过程传递数组类型的参数

用JDBC给存储过程传递数组类型的参数 收藏 A . 嵌套表 1. 声明数组类型 create or replace type tab_array is table of varchar2(38);暂时不要在包中声明该类型 2. 创建存储过程 -- 该例子存储过程是在包中创建的,包名 arraydemo procedure testArray(re...

disablebb的专栏 796

TMS320F28377D/F28379D CAN通信避坑指南:从寄存器配置到中断处理的完整流程

本文详细解析了TMS320F28377D/F28379D在CAN通信开发中的关键技术与避坑指南,涵盖时钟配置、寄存器初始化、中断处理等完整流程。针对工业控制和汽车电子应用,提供了实用的优化技巧和错误诊断方法,帮助开发者高效实现稳定可靠的CAN通信。

weixin_30954265的博客 492

电力系统设计实战:如何用ETAP绘制一次接线图并优化供电方案

本文详细介绍了如何使用ETAP软件进行电力系统一次接线图的绘制与供电方案优化。从基础环境配置到主接线拓扑构建,再到典型接线方式的对比分析与智能优化,为工业与民用电力系统设计提供实用指南。通过ETAP的可靠性分析和动态仿真功能,帮助工程师选择最优供电方案,提升系统可靠性和运维效率。

weixin_29250573的博客 205

【沉淀之华】你真的了解Hikari吗?NonRegisterDriver 引起Full GC问题排查之路,通用方法论介绍

在这个过程中由于这个释放资源的过程比其他对象释放是比较慢的,这样和容易在ParNew几次都回收不了,从而不得以超过gc年龄阈值,晋升到老年代,众所周知老年代对象只有在达到CMS阈值才会触发,换句话说到了老年代就是改对象"等死"的状态,随着越来越多的等死对象进入,就会加重GC负担【实际是重新标记阶段】,引起更长时间的STW。:这个相对影响不会太大,如果你的业务在高峰期都能用min-idle cover住,那么max-pool-size就只是限制系统的整体连接的创建上限,起到节省数据库资源的目的。

浮-沉 772

智能车竞赛环岛处理全解析:从图像识别到状态机控制

在嵌入式系统与自动控制领域,状态机是管理复杂系统行为、实现清晰逻辑流的核心设计模式。其原理在于定义一组有限的状态,以及触发状态迁移的事件和条件,从而将连续、模糊的过程分解为离散、确定的阶段。这种技术在机器人导航、工业自动化等场景中价值显著,尤其适用于需要应对多种场景、做出序列化决策的场合。在智能车竞赛中,赛道元素识别与路径跟踪是典型应用,其中环岛作为连续闭环的复杂场景,对系统的鲁棒性和决策连贯性提出了更高要求。通过图像处理提取赛道边线、宽度和曲率等特征,结合陀螺仪等传感器数据,可以构建一个专门处理环岛的状态

weixin_34033624的博客 561

使用JdbcTemplate模板时传递的参数Map和Object []数组

// 用户登录检测 public Map findUser(String username, String password) { //String sql = "select u.* from tbUser u where lower(u.UserName)=:UserName"; String sql = "select u.* from tbUser u where u.Us...

iteye_8954的博客 1900

【AI助力科研】不看一篇文献,手把手教你用chatGPT写文献综述!

这篇文章以【AI助力科研】为题,介绍了如何利用ChatGPT编写文献综述的方法。作者通过详细的指导,展示了不需要阅读大量文献,只需依靠ChatGPT模型即可完成文献综述的过程。该方法旨在提高科研工作者的效率,减少阅读负担,并且能够生成具有逻辑性和准确性的文献综述。

研究方向//图像视频处理/信号算法/红外无损检测/数值仿真/机器学习/优化算法/python/matlab 2万+

如何使用ArcGIS自动矢量化建筑

在显示的重分类对话框内,输入栅格选择提取单波段后的栅格,重分类分为三个层级,旧值分别为100-249、250-251和252-255,对应新值为1、2和3,旧值的最小和最大值不用修改,保持和原有的一致。同样在ArcToolbox内点击Spatial Analyst工具点击“重分类\重分类”,在重分类对话框内输入栅格选择之前重分类过后的图片,将旧值3的新值设置为1,这样分类后就只有两类。在显示的新建Shapefile对话框内,名称命名为折线,要素类型选择折线,点击编辑,设置坐标系为WGS 1984。

2165

jdbc的那点小事

jdbc的那点小事 2011年01月20日   1.Class.forName(),是根据类的名字将类装载到虚拟机里面了。把类装载到虚拟机里面和创建一个类的实例是不一样的,创建一个实例就会有一个   实例产生,但是把类装载到虚拟机里面就不一定会有实例产生。   2.通过DriverManager.registerDriver()和System.setProperty()方式,会直...

im127im 185

基于ROS2系统开发机器人

以上是基于ROS 2开发机器人的基本流程和一个简单的发布者-订阅者示例。通过这个流程,你可以逐步扩展和优化你的机器人系统,实现更复杂的功能。

xuweilin的博客 1495

【Spring】学习Spring框架那点小事儿

Spring 框架是一个开源的 Java 应用程序框架,它提供了全面的解决方案,用于开发企业级 Java 应用程序。Spring 框架的核心是一个轻量级的容器,它提供了依赖注入、AOP、事务管理等功能。Spring 框架还提供了许多其他的模块,如 MVC 框架、JDBC 抽象层、ORM 集成、消息传递、Web 服务等,这些模块可以单独使用,也可以组合使用,以满足不同的需求。Spring 框架的设计理念是“约定优于配置”,这意味着它提供了一套默认的配置,开发人员可以根据需要进行修改。

一个写了10年bug的程序员日常笔记。 1062

基于STM32的智能交通灯管理系统设计

本项目基于STM32微控制器设计了一个智能交通灯管理系统,通过多个传感器模块实现对交通灯的智能化控制。项目涉及硬件电路设计、传感器数据采集和逻辑控制实现,适用于城市交通路口或小区路口。该基于STM32的智能交通灯系统通过超声波传感器实现了流量监测,结合LED和蜂鸣器实现了自动交通信号控制,适用于城市或社区路口。基于STM32CubeMX生成的框架,编写红绿灯切换、流量监测和蜂鸣器控制的代码。以下为主循环,通过不断读取传感器数据,结合交通灯和蜂鸣器模块实现智能交通灯控制。

2401_88410555的博客 2063

基于C#的工业机器人上位机控制程序开发(搭配松下PLC+MC协议)

基于C#的工业机器人上位机控制程序开发,核心是上位机与松下PLC的MC协议通信和机器人控制的安全逻辑,而不是单纯的界面编写。在工控现场,程序的稳定性和安全性永远是第一位的——比如解耦通信逻辑、处理跨线程问题、添加安全互锁、记录操作日志,这些都是保证产线稳定运行的关键。新手在学习时,建议先跟着步骤搭建好环境,跑通基础的PLC通信和机器人控制功能,再逐步尝试进阶优化。数据模型化、通信封装化、逻辑模块化、UI解耦化。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1239

初始 JDBC

Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。!!

weixin_61635597的博客 496

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

JDBC是什么?

转载自:https://blog.youkuaiyun.com/qq_27093465/article/details/53411627 首先,在这纠正一个很多人都存在的问题,当然,就算我纠正了,也基本不会有几个人会改的。因为:习惯使然。就像我格言:一句"习惯就好",坑了多少人。 很多东西,每天都在用,都在看。但是,真当问起你的时候,你还真不一定知道。换句话说就是,你所知道的,你以为你知道的是正确的东西,压...

微仔的黄金小屋 2275

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

jdbc,叫的很顺口,但是你真的知道这个是什么意思吗?

首先,在这纠正一个很多人都存在的问题,当然,就算我纠正了,也基本不会有几个人会改的。因为:习惯使然。就像我格言:一句"习惯就好",坑了多少人。 很多东西,每天都在用,都在看。但是,真当问起你的时候,你还真不一定知道。换句话说就是,你所知道的,你以为你知道的是正确的东西,压根儿就是错误的。 这也就是所谓的人云亦云。 不要小看这个东西,想想自己有没有这么干过,看到个消息,也没考虑就,嗖嗖的转发了

请叫我大师兄 1万+

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

关于Mybatis的几件小事(一)

一、Mybatis简介 1.Mybatis简介 MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。 MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。 MyBatis可以使用简单的XML或注解用于配置和冤死映射,将接口和Java的POJO(Plain Old Java Objects,普通的java对象)映射成数据库中的记录。 2.为什么使用MyB...

jack199504的博客 340

JavaWeb-JDBC实现数据库更新操作(超简单入门版)

摘要:本文演示了使用JDBC修改MySQL数据库的完整流程。通过Maven引入MySQL驱动依赖,提供了一个标准的五步操作示例:1)注册驱动、2)获取连接、3)创建Statement、4)执行update语句(将id=1的用户年龄改为18)、5)释放资源。运行结果会显示受影响的行数,并附有建表SQL供测试使用。该示例简单清晰地展现了JDBC的基础用法,适合Java数据库操作初学者入门实践。

2402_84971234的博客 564

代码评审无小事,十项注意别忘记(下)

通过良好的接口规范设计,我们可以实现代码的高内聚、低耦合,提高代码的可维护性和可扩展性。从代码的接口规范角度来看,一个好的代码案例应该遵循一定的接口设计原则,例如单一职责原则(SRP)、开闭原则(OCP)和依赖倒置原则(DIP)等。请注意,以上只是一个简单的示例,实际的单元测试可能更加复杂,并且可能需要使用其他工具和技术来进行更全面的测试覆盖。请注意,该代码案例是一个简单的示例,实际应用中可能会更加复杂,需要根据具体需求和业务场景进行数据库设计和操作。日志应包含足够的信息,方便追踪和分析问题。

青轩桃李能几何,流光欺人忽蹉跎 572

MySQL JDBC SSL加密配置与避坑指南

SSL/TLS加密是保障数据库通信安全的核心技术,通过非对称加密实现身份认证与数据保密。其核心原理是利用数字证书体系建立可信通道,防范流量监听与中间人攻击。在金融、政务等对安全性要求高的领域,SSL加密不仅是技术选择,更是等保、PCI DSS等合规要求的必要条件。以MySQL数据库为例,通过配置JDBC的sslMode参数和证书体系,可实现从单向认证到双向认证的不同安全等级。实践中需注意证书生成规范、协议版本选择以及性能优化技巧,避免常见错误如证书链不完整或TLS协议不匹配。本文以MySQL 8.0企业版为

weixin_30326515的博客 384

MySQL JDBC驱动下载与配置全攻略:从Maven依赖到连接字符串避坑

JDBC(Java Database Connectivity)是Java语言中用于连接和操作数据库的标准API,它定义了一套通用的接口,允许开发者以统一的方式访问各种关系型数据库。其核心原理是通过数据库厂商提供的特定驱动实现,将标准的JDBC调用转换为数据库特有的通信协议。这项技术的价值在于实现了Java应用与数据库的解耦,提升了程序的可移植性和可维护性。在实际的工程实践中,无论是开发Web应用、大数据处理任务(如使用Flink或Sqoop),还是构建微服务,都离不开稳定可靠的数据库连接。其中,MySQL

weixin_33393017的博客 339
上一篇: 闲来无事,复习复习以前的东西---hibernate查询细节资料
下一篇: ActiveMQ 安全配置
java_min
博客等级 码龄18年 61粉丝 161原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值