JDBC的使用步骤 注册驱动 只做一次 无需手动注册,DriverManager会通过SPI注入
建立连接Connection Connection conn = DriverManager.getConnection(url, user, password);
url格式: JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&… User,password可以用“属性名=属性值”方式告诉数据库; 其他参数如:useUnicode=true&characterEncoding=GBK。
创建执行SQL的语句Statement Statement
1 2 Statement st = conn.createStatement(); st.executeQuery(sql);
PreparedStatement
1 2 3 4 String sql = “select * from table_name where col_name = ?”; PreparedStatement ps = conn.preparedStatement(sql); ps.setString(1 , “col_value”); ps.executeQuery();
处理执行结果ResultSet 1 2 3 4 5 6 ResultSet rs = statement.executeQuery(sql); While(rs.next()){ rs.getString(“col_name”); rs.getInt(“col_name”); }
释放资源 释放ResultSet, Statement,Connection.
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
使用JDBC来实现CRUD的操作 建表 在这里定义了一个用户信息表
1 2 3 4 5 6 7 CREATE TABLE `tb_user_info` ( `user_id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '主键' , `name` varchar (10 ) DEFAULT NULL COMMENT '姓名' , `gender` enum('男' ,'女' ) DEFAULT NULL COMMENT '性别' , `birthday` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '生日' , PRIMARY KEY (`user_id`) ) ENGINE= InnoDB AUTO_INCREMENT= 10000 DEFAULT CHARSET= utf8 COMMENT= '用户信息表'
Entity 与该表对应的Entity为:
JDBC的CRUD 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public class JDBCDemo { private Connection connection; public Connection connection (final String url, final String name, final String password) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver" ); Connection connection = DriverManager.getConnection(url, name, password); this .connection = connection; return connection; } public List <UserInfo> list (int p, int ps) throws SQLException { int start = p * ps; PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user_info limit " + start + "," + ps); ResultSet resultSet = preparedStatement.executeQuery(); ArrayList <UserInfo> list = new ArrayList <UserInfo>(); while (resultSet.next()) { int userId = resultSet.getInt("user_id" ); String name = resultSet.getString("name" ); Timestamp birthday = resultSet.getTimestamp("birthday" ); String gender = resultSet.getString("gender" ); UserInfo userInfo = new UserInfo (userId, name, gender, birthday); list.add(userInfo); } return list; } public List <UserInfo> findByName (String name) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user_info where name = ?" ); preparedStatement.setString(1 , name); ResultSet resultSet = preparedStatement.executeQuery(); ArrayList <UserInfo> list = new ArrayList <UserInfo>(); while (resultSet.next()) { int userId = resultSet.getInt("user_id" ); Timestamp birthday = resultSet.getTimestamp("birthday" ); String gender = resultSet.getString("gender" ); UserInfo userInfo = new UserInfo (userId, name, gender, birthday); list.add(userInfo); } return list; } public int addUser (UserInfo userInfo) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement("insert into tb_user_info values(default,?,?,?)" ); preparedStatement.setString(1 , userInfo.getName()); preparedStatement.setString(2 , userInfo.getGender()); preparedStatement.setTimestamp(3 , userInfo.getBirthday()); int influenceLines = preparedStatement.executeUpdate(); return influenceLines; } public int delete (int userId) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement("delete from tb_user_info where user_id = ?" ); preparedStatement.setInt(1 , userId); int influenceLines = preparedStatement.executeUpdate(); return influenceLines; } public int update (UserInfo userInfo) throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement("update tb_user_info set name=? , gender = ? , birthday = ? where user_id = ?" ); preparedStatement.setString(1 , userInfo.getName()); preparedStatement.setString(2 , userInfo.getGender()); preparedStatement.setTimestamp(3 , userInfo.getBirthday()); preparedStatement.setInt(4 , userInfo.getUserId()); int influenceLines = preparedStatement.executeUpdate(); return influenceLines; } }
测试样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 JDBCDemo jdbcDemo = new JDBCDemo ();jdbcDemo.connection("jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=UTF-8" , "root" , "123456" ); UserInfo obama = new UserInfo (0 , "奥巴马" , "男" , new Timestamp (System.currentTimeMillis()));UserInfo cirali = new UserInfo (0 , "希拉里" , "女" , new Timestamp (System.currentTimeMillis()));UserInfo trappes = new UserInfo (0 , "特朗普" , "女" , new Timestamp (System.currentTimeMillis()));System.out.println("[添加 奥巴马]" ); jdbcDemo.addUser(obama); System.out.println("[添加 希拉里]" ); jdbcDemo.addUser(cirali); System.out.println("[添加 特朗普]" ); jdbcDemo.addUser(trappes); System.out.println("\n 输出 所有用户信息:" ); List <UserInfo> list = jdbcDemo.list(0 , 10 );if (list != null ) { for (UserInfo userInfo : list) { System.out.println(userInfo); } } System.out.println("根据姓名查询用户信息" ); UserInfo tUserIndo = null ;List <UserInfo> resultList = jdbcDemo.findByName("特朗普" );if (resultList != null ) { tUserIndo = resultList.get(0 ); } System.out.println(tUserIndo); tUserIndo.setGender("男" ); System.out.println("[更新]" ); jdbcDemo.update(tUserIndo); System.out.println(tUserIndo); System.out.println("\n 输出 所有用户信息:" ); list = jdbcDemo.list(0 , 10 ); if (list != null ) { for (UserInfo userInfo : list) { System.out.println(userInfo); } } public static void free (ResultSet 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(); } } } }
输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [添加 奥巴马] [添加 希拉里] [添加 特朗普] 输出 所有用户信息: UserInfo{userId=10009, name='奥巴马', gender=男, birthday=2016-11-27 10:03:00.0} UserInfo{userId=10010, name='希拉里', gender=女, birthday=2016-11-27 10:03:00.0} UserInfo{userId=10011, name='特朗普', gender=女, birthday=2016-11-27 10:03:00.0} 根据姓名查询用户信息 UserInfo{userId=10011, name='特朗普', gender=女, birthday=2016-11-27 10:03:00.0} [更新] UserInfo{userId=10011, name='特朗普', gender=男, birthday=2016-11-27 10:03:00.0} 输出 所有用户信息: UserInfo{userId=10009, name='奥巴马', gender=男, birthday=2016-11-27 10:03:00.0} UserInfo{userId=10010, name='希拉里', gender=女, birthday=2016-11-27 10:03:00.0} UserInfo{userId=10011, name='特朗普', gender=男, birthday=2016-11-27 10:03:00.0}
注意
使用prepareStatement预处理执行SQL语句时,?的索引是从1开始的
JDBC中特殊数据类型的操作问题 第一个是日期问题 JDBC接收的时间类型是sql下的类型,与Java中的类型不同,通常需要转化。
如 java.sql.Date <—> java.util.Date
第二个问题就是大文本数据的问题 读写大文本数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static void insert () { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); String sql = "insert into clob_test(bit_text) values(?)" ; ps = conn.prepareStatement(sql); File file = new File ("src/com/weijia/type/ClubDemo.java" ); Reader reader = new BufferedReader (new FileReader (file)); ps.setCharacterStream(1 , reader, (int )file.length()); ps.executeUpdate(); reader.close(); }catch (Exception e){ e.printStackTrace(); }finally { JdbcUtils.free(rs,ps,conn); } }
1 2 Clob clob = rs.getClob(1 ); InputStream is = clob.getAsciiStream();
JDBC中事务的概念 JDBC中调用存储过程 JDBC来实现批处理功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static void createBatch () throws Exception{ Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); String sql = "insert user(name,birthday,money) values(?,?,?)" ; ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); for (int i = 0 ;i<100 ;i++){ ps.setString(1 ,"jiangwei" ); ps.setDate(2 ,new Date (System.currentTimeMillis())); ps.setFloat(3 ,400 ); ps.addBatch(); } ps.executeBatch(); }catch (Exception e){ e.printStackTrace(); }finally { JdbcUtils.free(rs, ps, conn); } }
JDBC中的滚动结果集和分页技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 static void test () throws Exception{ Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY); rs = st.executeQuery("select id,name,money,birthday from user" ); rs.absolute(2 ); System.out.println("id=" +rs.getInt("id" )+"\tname=" +rs.getString("name" )+"\tbirthday=" +rs.getDate("birthday" )+"\tmoney=" +rs.getFloat("money" )); rs.beforeFirst(); rs.afterLast(); rs.isFirst(); rs.isLast(); rs.isAfterLast(); rs.isBeforeFirst(); }catch (Exception e){ e.printStackTrace(); }finally { JdbcUtils.free(rs,st,conn); } }
JDBC中的可更新以及对更新敏感的结果集操作 元数据的相关知识 数据库的元数据信息 查询参数的元数据信息 结果集中元数据信息 JDBC中的数据源 JDBC中CRUD的模板模式 Spring框架中的JdbcTemplate 加强版的JdbcTemplate NamedParameterJdbcTemplate SimpleJdbcTemplate 事务 批量 常见错误
java.sql.SQLException: Incorrect string value: '\xE3\x80\x90\xE9\x80\x9A...' for column 'msg' at row 1 编码问题: 检查数据库编码,数据表编码,列编码以及连接数据库使用的characterEncoding
链接数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.seecen.stream;import java.sql.*;public class TestJDBC { public static void main (String[] args) { ResultSet rs = null ; Statement stmt = null ; Connection conn = null ; try { Class.forName("oracle.jdbc.driver.OracleDriver" ); conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.1:1521:yuewei" , "scott" , "tiger" ); stmt = conn.createStatement(); rs = stmt.executeQuery("select * from dept" ); while (rs.next()) { System.out.println(rs.getString("deptno" )); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null ) { rs.close(); rs = null ; } if (stmt != null ) { stmt.close(); stmt = null ; } if (conn != null ) { conn.close(); conn = null ; } } catch (SQLException e) { e.printStackTrace(); } } } }
调用存储过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.huawei.interview.lym;import java.sql.CallableStatement;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Types;public class JdbcTest { public static void main (String[] args) { Connection cn = null ; CallableStatement cstmt = null ; try { Class.forName("com.mysql.jdbc.Driver" ); cn = DriverManager.getConnection("jdbc:mysql:///test" ,"root" ,"root" ); cstmt = cn.prepareCall("{call insert_Student(?,?,?)}" ); cstmt.registerOutParameter(3 ,Types.INTEGER); cstmt.setString(1 , "wangwu" ); cstmt.setInt(2 , 25 ); cstmt.execute(); System.out.println(cstmt.getString(3 )); } catch (Exception e) { e.printStackTrace(); } finally { try { if (cstmt != null ) cstmt.close(); if (cn != null ) cn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
JDBC中的PreparedStatement相比Statement的好处 1)提高性能:在使用preparedStatement对象执行sql时候,命令被数据库编译和解析,然后被放到命令缓冲区,然后每当执行同一个preparedStatement时候,他就被再解析一次,但不会在编译,在缓冲区中可以发现预编译的命令,并且可以重新使用。 如果你要写Insert update delete 最好使用preparedStatement,在有大量用户的企业级应用软件中,经常会执行相同的sql,使用preparedStatement会增加整体的性能。 2)安全性:PreparedStatement 可以防止sql注入。
JDBC原理 调用Class.forName("com.mysql.jdbc.Driver"); 加载mysql的驱动类进内存,那么就会在DriverManager中注册自己,注册的意思简单来说就是DriverManager中保持一个Driver引用指向了自己,但是具体的实现可能不同。
然后嗲用DriverManager.getConnection方法得到连接对象, 这里运用到了简单工厂方法,即根据传进去得参数来具体实例化哪个驱动类。
可能是mysql的驱动类, 也可能是Oracle的驱动类, 具体的由传进去的参数来决定。
当得到Connection对象后就没DriverManager和Driver类什么事了。
Connection一个接口,但是它指向了具体的Connection子类对象。
通过Connection中定义的接口,就能够访问数据库了。
所以总得来说,如果要改变当前使用的数据库,那么只需要改变两个地方,
Class.forName(具体的参数)
DriverManager.getConnection(具体的参数)
所以我们可以在配置文件中配置这两个参数,那么我们就可以在程序运行的时候动态地改变所使用的数据库,只需要更改配置文件就行了。
当然了,程序肯定要有数据库第三方jar包。
JDBC 字段判空问题 https://github.com/openjdk/jdk/blob/master/src/java.sql/share/classes/java/sql/ResultSet.java#L222
1 2 3 4 5 6 7 8 9 10 11 12 13 14 boolean wasNull () throws SQLException;
注意: wasNull方法只是判断上一个值是否为空,在使用时需要先获取值,再判空。
错误使用的样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static Object getField ( ResultSet resultSet, int index, int type ) { if (resultSet.wasNull) { return null ; } if (type == Type.LONG) { return resultSet.getLong(index + 1 ); } else if (type == Type.NULL) { return null ; } ... }
参考文献:
[J2EE学习篇之–JDBC详解 ][http://blog.csdn.net/jiangwei0910410003/article/details/26164629 ]