• 1. JDBC讲师:李勇北京传智播客教育 www.itcast.cn
  • 2. 简介JDBC(Java Data Base Connectivity,java数据库连接),由一些接口和类构成的API。 J2SE的一部分,由java.sql,javax.sql包组成。北京传智播客教育 www.itcast.cn
  • 3. 简介应用程序、JDBC API、数据库驱动及数据库之间的关系 北京传智播客教育 www.itcast.cn
  • 4. 连接数据的步骤注册驱动 (只做一次) 建立连接(Connection) 创建执行SQL的语句(Statement) 执行语句 处理执行结果(ResultSet) 释放资源 快速起步示例北京传智播客教育 www.itcast.cn
  • 5. 注册驱动Class.forName(“com.mysql.jdbc.Driver”); 推荐这种方式,不会对具体的驱动类产生依赖。 DriverManager.registerDriver(com.mysql.jdbc.Driver); 会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖。 System.setProperty(“jdbc.drivers”, “driver1:driver2”); 虽然不会对具体的驱动类产生依赖;但注册不太方便,所以很少使用。 驱动类型(四种类型)北京传智播客教育 www.itcast.cn
  • 6. 建立连接(Connection)Connection conn = DriverManager.getConnection(url, user, password); url格式: JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&… User,password可以用“属性名=属性值”方式告诉数据库; 其他参数如:useUnicode=true&characterEncoding=GBK。北京传智播客教育 www.itcast.cn
  • 7. 创建执行SQL的语句(Statement)Statement Statement st = conn.createStatement(); st.executeQuery(sql); PreparedStatement String sql = “select * from table_name where col_name=?”; PreparedStatement ps = conn.preparedStatement(sql); ps.setString(1, “col_value”); ps.executeQuery();北京传智播客教育 www.itcast.cn
  • 8. 处理执行结果(ResultSet)ResultSet rs = statement.executeQuery(sql); While(rs.next()){ rs.getString(“col_name”); rs.getInt(“col_name”); //… }北京传智播客教育 www.itcast.cn
  • 9. 释放资源释放ResultSet, Statement,Connection. 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。北京传智播客教育 www.itcast.cn
  • 10. 基本的CRUD(创建、读取、更新、删除) 模板代码 Connection conn = null; Statement st=null; ResultSet rs = null; try { //获得Connection //创建Statement //处理查询结果ResultSet } finally { //释放资源ResultSet, Statement,Connection }北京传智播客教育 www.itcast.cn
  • 11. 创建增加对应SQL的INSERT,返回增加成功的行(记录)数 conn = getConnection(); Statement st = conn.createStatement(); String sql=“insert into user(name, age,regist_date )” + “values(‘name’, 10, now())”; int i = st.executeUpdate(sql); //i为插入的记录数北京传智播客教育 www.itcast.cn
  • 12. 读取读取(查询)对应SQL的SELECT,返回查询结果 conn = getConnection(); st = conn.createStatement(); String sql = "select id, name, age,regist_date from user"; rs = st.executeQuery(sql); while (rs.next()) { System.out.print(rs.getInt("id") + " \t\t "); System.out.print(rs.getString("name") + " \t\t "); System.out.print(rs.getInt("age") + " \t\t "); System.out.print(rs.getTimestamp("regist_date") + " \t\t "); System.out.println(); }北京传智播客教育 www.itcast.cn
  • 13. 更新更新(修改)对应SQL的UPDATE,返回被修改的行(记录)数 conn = getConnection(); Statement st = conn.createStatement(); String sql=“update person set name='new name‘”; int i = st.executeUpdate(sql); //i为符合条件的记录数北京传智播客教育 www.itcast.cn
  • 14. 删除删除对应SQL的DELETE,返回被删除的行(记录)数 conn = getConnection(); Statement st = conn.createStatement(); String sql=“delete from user where id=1”; int i = st.executeUpdate(sql); //i为删掉的记录数北京传智播客教育 www.itcast.cn
  • 15. CRUD总结增、删、改用Statement.executeUpdate来完成,返回整数(匹配的记录数),这类操作相对简单。 查询用Statement.executeQuery来完成,返回的是ResultSet对象,ResultSet中包含了查询的结果;查询相对与增、删、改要复杂一些,因为有查询结果要处理。北京传智播客教育 www.itcast.cn
  • 16. SQL注入,PreparedStatement和Statement 在SQL中包含特殊字符或SQL的关键字(如:' or 1 or ')时Statement将出现不可预料的结果(出现异常或查询的结果不正确),可用PreparedStatement来解决。 PreperedStatement(从Statement扩展而来)相对Statement的优点: 1.没有SQL注入的问题。 2.Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。 3.数据库和驱动可以对PreperedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)。 北京传智播客教育 www.itcast.cn
  • 17. 数据类型详细信息见java.sql.Types 几种特殊且比较常用的类型 1.DATA,TIME,TIMESTAMP date,time,datetime 存:ps.setDate(i,d); ps.setTime(i,t); ps.setTimestamp(i, ts); 取:rs.getDate(i); rs.getTime(i); rs.getTimestamp(i); 2.CLOB  text 存:ps.setCharacterStream(index, reader, length); ps.setString(i, s); 取:reader = rs. getCharacterStream(i); reader = rs.getClob(i).getCharacterStream(); string = rs.getString(i); 3.BLOB  blob 存:ps.setBinaryStream(i, inputStream, length); 取:rs.getBinaryStream(i); rs.getBlob(i).getBinaryStream(); 北京传智播客教育 www.itcast.cn
  • 18. 一个简单用户相关的数据访问层 J2EE三层架构简介 表示层 、业务逻辑层、数据访问层,三层之间用接口隔离。 定义domain对象User,定义存取用户的接口 用JDBC实现接口 用配置文件(properties)和反射实现与具体类的耦合 北京传智播客教育 www.itcast.cn
  • 19. 事务(ACID)原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。 一致性(consistency):在事务处理执行前后,数据库是一致的(数据库数据完整性约束)。 隔离性(isolcation):一个事务处理对另一个事务处理的影响。 持续性(durability):事务处理的效果能够被永久保存下来 。 connection.setAutoCommit(false);//打开事务。 connection.commit();//提交事务。 connection.rollback();//回滚事务。北京传智播客教育 www.itcast.cn
  • 20. 事务(SavePoint)当只想撤销事务中的部分操作时可使用SavePoint SavePoint sp = connection.setSavepoint(); connection.rollerbak(sp);connection.commit();北京传智播客教育 www.itcast.cn
  • 21. 事务(JTA)跨越多个数据源的事务,使用JTA容器实现事务。 分成两阶段提交。 javax.transaction.UserTransaction tx = (UserTransaction)ctx.lookup(“jndiName"); tx.begin(); //connection1 connection2 (可能来自不同的数据库)… tx.commit();//tx.rollback();北京传智播客教育 www.itcast.cn
  • 22. 隔离级别多线程并发读取数据时的正确性 connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); V:可能出现,X:不会出现隔离级别 脏读 不可重复读 幻读 读未提交(Read uncommitted)VVV读已提交(Read committed)xVV可重复读(Repeatable read)xxV可串行化(Serializable )xxx北京传智播客教育 www.itcast.cn
  • 23. 存储过程存储过程 CallableStatement(从PreperedStatement扩展来) cs = connection.prepareCall(“{call psname(?,?,?)}”); cs.registerOutParameter(index, Types.INTEGER); cs.setXXX(i, xxxx); cs.executeUpdate(); int id=cs.getInt(index);北京传智播客教育 www.itcast.cn
  • 24. 其他的几个APIPreparedStatement.getGeneratedKeys() PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.executeUpdate(); ResultSet rs = st.getGeneratedKeys();rs.getInt(1); 批处理,可以大幅度提升大量增、删、改的速度。 PreparedStatement.addBatch(); PreparedStatement.executeBatch();北京传智播客教育 www.itcast.cn
  • 25. 其他的几个API可滚动的结果集 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(); 可更新的结果集 conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); rs.updateString("col name", "new value"); rs.updateRow();北京传智播客教育 www.itcast.cn
  • 26. DatabaseMetaData和ParameterMetaDataDatabaseMetaData meta = connection.getMetaData(); 通过DatabaseMetaData可以获得数据库相关的信息如:数据库版本、数据库名、数据库厂商信息、是否支持事务、是否支持某种事务隔离级别,是否支持滚动结果集等。 ParameterMetaData pmd = preparedStatement.getParameterMetaData(); 通过 ParameterMetaData可以获得参数信息。北京传智播客教育 www.itcast.cn
  • 27. ResultSetMetaDataResultSetMetaData meta = rs.getMetaData(); 通过ResultSetMetaData可以获得结果有几列、各列名、各列别名、各列类型等。 可以将ResultSet放入Map(key:列名 value:列值)。 用反射ResultSetMetaData将查询结果读入对象中(简单的O/RMapping) 1)让SQL语句中列别名和要读入的对象属性名一样; 2)通过ResultSetMetaData获得结果列数和列别名; 3)通过反射将对象的所有setXxx方法找到; 4)将3)找到的方法setXxx和2)找到的列别名进行匹配(即方法中的xxx于列别名相等); 5)由上一步找到的方法和列别名对应关系进行赋值 Method.invoke(obj, rs.getObject(columnAliasName));北京传智播客教育 www.itcast.cn
  • 28. 数据源和连接池DataSource用来取代DriverManager来获取Connection; 通过DataSource获得Connection速度很快; 通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),他的close方法已经被修改。 一般DataSource内部会用一个连接池来缓存Connection,这样可以大幅度提高数据库的访问速度; 连接池可以理解成一个能够存放Connection的Collection; 我们的程序只和DataSource打交道,不会直接访问连接池;北京传智播客教育 www.itcast.cn
  • 29. 一个简单的数据源实现使用代理模式的Connection(核心代码) class MyConnection implements Connection{ private Connection realConn; private LinkedList connPool; MyConnection(Connection rConn, LinkedList cPool){ this.realConn=rConn; this.connPool=cPool; } public void close(){ this.connPool.addLast(this); } //…. }北京传智播客教育 www.itcast.cn
  • 30. 一个简单的数据源实现DataSource(核心代码) class MyDataSource implements DataSource{ private LinkedList connPool = new Vector(); public Connection getConneciton (){ if(this.connPool.size()>0) return this.connPool.removeFirst(0); return createConnection(); } private Connection createConnection(){ Connection realConn = DriverManager.getConnection(); Connection myConn = new MyConnection(realConn,this.connPool); return myConn; } //…. }北京传智播客教育 www.itcast.cn
  • 31. 常用的开源实现DBCP使用DBCP必须用的三个包: commons-dbcp-1.2.1.jar, commons-pool-1.2.jar, commons-collections-3.1.jar。 配置参数。 Java API: BasicDataSourceFactory.createDataSource( properties); 北京传智播客教育 www.itcast.cn
  • 32. 使用继承优化JDBC代码(模板模式)优化查询操作,区分开变化和不变的部分: sql和ResultSet的处理是变化部分,创建和释放资源部分是不变部分。 提取超类,将不变部分放入超类,变化部分留给子类实现。 超类的主要代码: public Object find(String sql, Object[] args) { … rs = ps.executeQuery(); if (rs.next()) return rowMapper(rs); … } protected abstract Object rowMapper(ResultSet rs);北京传智播客教育 www.itcast.cn
  • 33. 使用组合优化JDBC代码(策略模式)优化查询操作,区分开变化和不变的部分: sql和ResultSet的处理是变化部分,创建和释放资源部分是不变部分。 提取接口封装变化部分。 JDBC操作主要代码: public Object find(String sql, Object[] args, RowMapper rowMapper) { … rs = ps.executeQuery(); if (rs.next())return rowMapper.mapRow(rs); … } 回调接口: public interface RowMapper { public Object mapRow(ResultSet rs) throws SQLException; }北京传智播客教育 www.itcast.cn
  • 34. Spring的JdbcTemplate查询带有参数,和行映射方法: public Object queryForObject(String sql, Object[] args, RowMapper rowMapper),使用自定义的UserRowMapper完成映射。 一个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(key:字段名或别名,value:列值)。 public List queryForList(String sql, Object[] args)返回多Map。北京传智播客教育 www.itcast.cn
  • 35. Spring的JdbcTemplate更新public int update(String sql, Object[] args)。 插入数据并获得结果: public Object execute(ConnectionCallback action) 其他方法简介北京传智播客教育 www.itcast.cn
  • 36. Spring的NamedParameterJdbcTemplateNamedParameterJdbcTemplate内部包含了一个JdbcTemplate,所以JdbcTemplate能做的事情NamedParameterJdbcTemplate都能干; NamedParameterJdbcTemplate相对于JdbcTemplate主要增加了参数可以命名的功能。 public Object queryForObject(String sql, Map paramMap, RowMapper rowMapper) public Object queryForObject(String sql, SqlParameterSource paramSource, RowMapper rowMapper) SqlParameterSource的两个主要实现MapSqlParameterSource 和BeanPropertySqlParameterSource public int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder)保存数据获得主键。北京传智播客教育 www.itcast.cn
  • 37. Spring的SimpleJdbcTemplateSimpleJdbcTemplate内部包含了一个NamedParameterJdbcTemplate;所以NamedParameterJdbcTemplate能做的事情SimpleJdbcTemplate都能干,SimpleJdbcTemplate相对于NamedParameterJdbcTemplate主要增加了JDK5.0的泛型和可变长度参数支持。 public List query(String sql, ParameterizedRowMapper rm, Object... args) public T queryForObject(String sql, ParameterizedRowMapper rm, SqlParameterSource args) public List query(String sql, ParameterizedRowMapper rm, SqlParameterSource args) getJdbcOperations返回的是JdbcOperations(实现JdbcTemplate) getNamedParameterJdbcOperations返回的是NamedParameterJdbcOperations(实现是NamedParameterJdbcTemplate)北京传智播客教育 www.itcast.cn
  • 38. 实现UserDao接口实现UserDao接口 修改daoconfig.properties文件,切换UserDao的实现。 测试UserDao的新的实现类。北京传智播客教育 www.itcast.cn