脑子太笨,必须得记录下来一些文字,方便回来查询。

这是我的第二次学习JDBC的笔记,看的是传智播客——李勇老师的JDBC系列,已看到第23集。

分析在实际项目中该如何应用JDBC

一个简单用户相关的数据访问层

  1. J2EE三层架构简介:表示层 、业务逻辑层、数据访问层,三层之间用接口隔离。
  2. 定义domain对象User,定义存取用户的接口。

  3. 用JDBC实现接口。

  4. 用配置文件(properties)和反射实现与具体类的耦合。

用图来表示:

在真实的项目开发中,我们能不能返回ResultSet对象呢? 如下例代码:

  1. static ResultSet read() throws SQLException {
  2. Connection conn = null;
  3. Statement st = null;
  4. ResultSet rs = null;
  5. try {
  6. //2.建立连接
  7. conn = JdbcUtils.getConnection();
  8. //3.创建语句
  9. st = conn.createStatement();
  10. //4.执行语句
  11. rs = st.executeQuery("select id,name,birthday,money from user");
  12. //5.处理结果
  13. while(rs.next()) {
  14. System.out.println(rs.getObject("id")+"\t"+rs.getObject("name")+
  15. "\t"+rs.getObject("birthday")+"\t"+rs.getObject("money"));
  16. }
  17. return rs;//连接关闭,Statement、ResultSet都失效了。
  18. } finally {
  19. JdbcUtils.free(rs, st, conn);
  20. }
  21. }

在main()方法中调用:

  1. ResultSet rs = read();

这个其实是不可以的,当Connection关闭之后,ResultSet中的数据你就拿不到了(Statement、ResultSet都失效了)。如果我们要进行传值需要定义个domain对象。

我们重点关注数据访问层的代码如何书写

1、定义domain对象User以及存取用户的接口:

  1. import cn.itcast.jdbc.domain.User;
  2.  
  3. public interface UserDao {
  4. public void addUser(User user);
  5.  
  6. public User getUser(int userId);
  7.  
  8. public User findUser(String loginName, String password);
  9.  
  10. public void update(User user);
  11.  
  12. public void delete(User user);
  13. }
  1. import java.util.Date;
  2.  
  3. public class User {
  4. private int id;
  5. private String name;
  6. private Date birthday;
  7. private float money;
  8.  
  9. public int getId() {
  10. return id;
  11. }
  12. public void setId(int id) {
  13. this.id = id;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public Date getBirthday() {
  22. return birthday;
  23. }
  24. public void setBirthday(Date birthday) {
  25. this.birthday = birthday;
  26. }
  27. public float getMoney() {
  28. return money;
  29. }
  30. public void setMoney(float money) {
  31. this.money = money;
  32. }
  33.  
  34. }

2、用JDBC实现接口。在实现的过程中,准确一点说会出现SQLException。关于这个异常的处理,很多工作2年的程序员都不知道怎么样处理,所以对于此异常,定当重视,通常我们结合Service层讲解DAO层的异常处理的方式。

就拿addUser(user)方法来说,中间是有可能出现SQLException的,在处理异常的时候,不能catch住异常之后什么都不做,最起码要打印一下堆栈,最好的方式是将这个异常转化为RuntimeException抛出。做法如下:

建立一个DaoException继承RuntimeException,如下:

  1. public class DaoException extends RuntimeException {
  2.  
  3. /**
  4. *
  5. */
  6. private static final long serialVersionUID = 1L;
  7.  
  8. public DaoException() {
  9. // TODO Auto-generated constructor stub
  10. }
  11.  
  12. public DaoException(String message) {
  13. super(message);
  14. // TODO Auto-generated constructor stub
  15. }
  16.  
  17. public DaoException(Throwable cause) {
  18. super(cause);
  19. // TODO Auto-generated constructor stub
  20. }
  21.  
  22. public DaoException(String message, Throwable cause) {
  23. super(message, cause);
  24. // TODO Auto-generated constructor stub
  25. }
  26.  
  27. public DaoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
  28. super(message, cause, enableSuppression, writableStackTrace);
  29. // TODO Auto-generated constructor stub
  30. }
  31.  
  32. }

之后JDBC实现接口的过程中,就抛出DaoException异常,如下(代码太多,折叠之):

  1. import java.sql.Connection;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6.  
  7. import cn.itcast.jdbc.JdbcUtils;
  8. import cn.itcast.jdbc.dao.DaoException;
  9. import cn.itcast.jdbc.dao.UserDao;
  10. import cn.itcast.jdbc.domain.User;
  11.  
  12. public class UserDaoJdbcImpl implements UserDao {
  13.  
  14. @Override
  15. public void addUser(User user) {
  16. Connection conn = null;
  17. PreparedStatement ps = null;
  18. ResultSet rs = null;
  19. try {
  20. //2.建立连接
  21. conn = JdbcUtils.getConnection();
  22. //3.创建语句
  23. String sql = "insert into user (name,birthday,money) values (?,?,?)";
  24. ps = conn.prepareStatement(sql);
  25. ps.setString(1, user.getName());
  26. ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
  27. ps.setFloat(3, user.getMoney());
  28. //4.执行语句
  29. ps.executeUpdate();
  30. } catch(SQLException e) {
  31. throw new DaoException(e.getMessage(), e);
  32. } finally {
  33. JdbcUtils.free(rs, ps, conn);
  34. }
  35. }
  36.  
  37. @Override
  38. public User getUser(int userId) {
  39. Connection conn = null;
  40. PreparedStatement ps = null;
  41. ResultSet rs = null;
  42. User user = null;
  43. try {
  44. //2.建立连接
  45. conn = JdbcUtils.getConnection();
  46. //3.创建语句
  47. String sql = "select id,name,birthday,money from user where id = ?";
  48. ps = conn.prepareStatement(sql);
  49. ps.setInt(1, userId);
  50. //4.执行语句
  51. rs = ps.executeQuery();
  52. //5.处理结果
  53. while(rs.next()) {
  54. /*
  55. * 优化①处
  56. */
  57. user = mappingUser(rs);
  58. }
  59. } catch(SQLException e) {
  60. throw new DaoException(e.getMessage(), e);
  61. } finally {
  62. JdbcUtils.free(rs, ps, conn);
  63. }
  64. return user;
  65. }
  66.  
  67. @Override
  68. public User findUser(String loginName, String password) {
  69. Connection conn = null;
  70. PreparedStatement ps = null;
  71. ResultSet rs = null;
  72. User user = null;
  73. try {
  74. //2.建立连接
  75. conn = JdbcUtils.getConnection();
  76. //3.创建语句
  77. String sql = "select id,name,birthday,money from user where name = ?";
  78. ps = conn.prepareStatement(sql);
  79. ps.setString(1, loginName);
  80. //4.执行语句
  81. rs = ps.executeQuery();
  82. //5.处理结果
  83. while(rs.next()) {
  84. /*
  85. * 优化①处
  86. */
  87. user = mappingUser(rs);
  88. }
  89. } catch(SQLException e) {
  90. throw new DaoException(e.getMessage(), e);
  91. } finally {
  92. JdbcUtils.free(rs, ps, conn);
  93. }
  94. return user;
  95. }
  96.  
  97. private User mappingUser(ResultSet rs) throws SQLException {
  98. User user = new User();
  99. user.setId(rs.getInt("id"));
  100. user.setName(rs.getString("name"));
  101. user.setMoney(rs.getFloat("money"));
  102. user.setBirthday(rs.getDate("birthday"));
  103. return user;
  104. }
  105.  
  106. @Override
  107. public void update(User user) {
  108. Connection conn = null;
  109. PreparedStatement ps = null;
  110. ResultSet rs = null;
  111. try {
  112. //2.建立连接
  113. conn = JdbcUtils.getConnection();
  114. //3.创建语句
  115. String sql = "update user set name = ?,birthday = ?,money = ? where id = ?";
  116. ps = conn.prepareStatement(sql);
  117. ps.setString(1, user.getName());
  118. ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
  119. ps.setFloat(3, user.getMoney());
  120. ps.setInt(4, user.getId());
  121. //4.执行语句
  122. ps.executeUpdate();
  123. } catch(SQLException e) {
  124. throw new DaoException(e.getMessage(), e);
  125. } finally {
  126. JdbcUtils.free(rs, ps, conn);
  127. }
  128. }
  129.  
  130. @Override
  131. public void delete(User user) {
  132. Connection conn = null;
  133. Statement st = null;
  134. ResultSet rs = null;
  135. try {
  136. //2.建立连接
  137. conn = JdbcUtils.getConnection();
  138. //3.创建语句
  139. st = conn.createStatement();
  140. //4.执行语句
  141. String sql = "delete from user where id = "+user.getId();
  142. st.executeUpdate(sql);
  143. } catch(SQLException e) {
  144. throw new DaoException(e.getMessage(), e);
  145. } finally {
  146. JdbcUtils.free(rs, st, conn);
  147. }
  148. }
  149.  
  150. }

用配置文件(properties)和反射实现与具体类的耦合(?),此时就要用到工厂模式了。

DaoFactory类如下(视频说代码很经典,谁知道呢?):

  1. import java.io.InputStream;
  2. import java.util.Properties;
  3.  
  4. public class DaoFactory {
  5. /*
  6. * 注意两个static属性之间的顺序
  7. * 如果搞反,那么属性userDao始终为空!
  8. */
  9. private static UserDao userDao = null;
  10. private static DaoFactory instance = new DaoFactory();
  11.  
  12. private DaoFactory() {
  13. try {
  14. Properties prop = new Properties();
  15.  
  16. /*
  17. * 方式一
  18. * 缺点:路径是写死的,如果我们的配置文件改变了地方,就找不到文件了 。
  19. */
  20. //FileInputStream fis = new FileInputStream("src/daoconfig.properties");
  21.  
  22. /*
  23. * 方式二
  24. * 优点:用类加载器(马丹,涉及到反射技术,又是一个要补的地方)的方法
  25. * 来得到一个文件的输入流,只要这个文件在classpath路径下(其实编译的xxx.class文件和配置文件
  26. * 就在根目录下bin目录中,而次bin目录被自动加载到classpath变量值中(别问我why?我也不知道))就能找到。
  27. */
  28. InputStream fis = DaoFactory.class.getClassLoader()//类加载器(???)
  29. .getResourceAsStream("daoconfig.properties");//daoconfig.properties只要在classpath路径下,就可以加载进来,更加灵活
  30. prop.load(fis);
  31. String userDaoClass = prop.getProperty("userDaoClass");
  32. /*
  33. * 涉及到反射技术
  34. */
  35. Class clazz = Class.forName(userDaoClass);//将类加载到JVM
  36. userDao = (UserDao)clazz.newInstance();//实例化一个对象
  37. //System.out.println(userDao);
  38. } catch (Throwable e) {
  39. throw new ExceptionInInitializerError(e);
  40. }
  41.  
  42. }
  43. public static DaoFactory getInstance() {
  44. return instance;
  45. }
  46.  
  47. public UserDao getUserDao() {
  48. //System.out.println(userDao);
  49. return userDao;
  50. }
  51. }

整体的框架图应该如这幅模样:

JDBC事务处理

关于事务,我已经在《浅谈事务》一文中详细记录过,在此不赘述了。我们只关注JDBC是如何处理事务的。

示例1(用代码来体现):

  1. import java.sql.Connection;
  2. import java.sql.ResultSet;
  3. import java.sql.SQLException;
  4. import java.sql.Statement;
  5.  
  6. public class TxIsolateTest {
  7. public static void main(String[] args) throws SQLException {
  8. test();
  9. }
  10.  
  11. static void test() throws SQLException {
  12. Connection conn = null;
  13. Statement st = null;
  14. ResultSet rs = null;
  15. try {
  16. conn = JdbcUtils.getConnection();
  17. /*
  18. * 设置事务为手动提交,一说也叫打开事务。
  19. */
  20. conn.setAutoCommit(false);
  21. /*
  22. * 设置事务的隔离级别,这是一个超麻烦的问题,李勇老师也是敷衍过去!
  23. */
  24. conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
  25.  
  26. String sql = "update user set money = money - 10 where id = 1";
  27. st = conn.createStatement();
  28. st.executeUpdate(sql);
  29.  
  30. sql = "select money from user where id = 2";
  31. rs = st.executeQuery(sql);
  32. float money = 0.0f;
  33. if(rs.next()) {
  34. money = rs.getFloat("money");
  35. }
  36. if(money > 400)
  37. throw new RuntimeException("已经超过最大值!");
  38. sql = "update user set money = money + 10 where id = 2";
  39. st.executeUpdate(sql);
  40. /*
  41. * 提交事务
  42. */
  43. conn.commit();
  44. } catch (SQLException e) {
  45. if(conn != null)
  46. /*
  47. * 回滚事务,rollback()中不加任何参数,即为回滚所有操作
  48. */
  49. conn.rollback();
  50. throw e;
  51. } finally {
  52. JdbcUtils.free(rs, st, conn);
  53. }
  54. }
  55. }

示例2:

  1. import java.sql.Connection;
  2. import java.sql.ResultSet;
  3. import java.sql.SQLException;
  4. import java.sql.Savepoint;
  5. import java.sql.Statement;
  6.  
  7. public class SavePointTest {
  8. public static void main(String[] args) throws SQLException {
  9. test();
  10. }
  11.  
  12. static void test() throws SQLException {
  13. Connection conn = null;
  14. Statement st = null;
  15. ResultSet rs = null;
  16. Savepoint sp = null;
  17. try {
  18. conn = JdbcUtils.getConnection();
  19. conn.setAutoCommit(false);
  20. st = conn.createStatement();
  21. String sql = "update user set money = money - 10 where id = 1";
  22. st.executeUpdate(sql);
  23. /*
  24. * 设置保存点
  25. */
  26. sp = conn.setSavepoint();
  27.  
  28. sql = "update user set money = money - 10 where id = 3";
  29. st.executeUpdate(sql);
  30.  
  31. sql = "select money from user where id = 2";
  32. rs = st.executeQuery(sql);
  33. float money = 0.0f;
  34. if(rs.next()) {
  35. money = rs.getFloat("money");
  36. }
  37. if(money > 300)
  38. throw new RuntimeException("已经超过最大值!");
  39. sql = "update user set money = money + 10 where id = 2";
  40. st.executeUpdate(sql);
  41. conn.commit();
  42. } catch (RuntimeException e) {
  43. if(conn != null && sp != null) {
  44. conn.rollback(sp);//回滚到保存点上去,再提交,这样保证到保存点之前那些操作都是有效的(只回滚一部分操作)
  45. conn.commit();
  46. }
  47. throw e;
  48. } catch (SQLException e) {
  49. if(conn != null)
  50. conn.rollback();
  51. throw e;
  52. } finally {
  53. JdbcUtils.free(rs, st, conn);
  54. }
  55. }
  56. }

JTA分布式事务的简要介绍(李勇老师也是敷衍过去了)

跨越多个数据源的事务,使用JTA容器(是啥,不知道?)实现事务。

分成两阶段提交。

代码格式总是如此:

  1. javax.transaction.UserTransaction tx = (UserTransaction)ctx.lookup(“jndi(?)Name");
  2. tx.begin();
  3. //connection1 connection2 (可能来自不同的数据库)…
  4. tx.commit();//tx.rollback();

JDBC第二次学习的更多相关文章

  1. 20145213《Java程序设计》第二周学习总结

    20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...

  2. 20145330孙文馨 《Java程序设计》第二周学习总结

    20145330孙文馨第二周学习总结 第二周相比于第一周对java语言有了深一点的了解,也意识到多敲代码才是学习计算机语言的最好方法. 教材内容总结 类型.变量与运算符 *基本类型 整数(short. ...

  3. 20145337 《Java程序设计》第二周学习总结

    20145337 <Java程序设计>第二周学习总结 教材学习内容总结 Java可分基本类型与类类型: 基本类型分整数(short.int.long).字节(byte).浮点数(float ...

  4. 20135328信息安全系统设计基础第二周学习总结(vim、gcc、gdb)

    第三周学习笔记 学习计时:共8小时 读书:1 代码:5 作业:1 博客:7 一.学习目标 熟悉Linux系统下的开发环境 熟悉vi的基本操作 熟悉gcc编译器的基本原理 熟练使用gcc编译器的常用选项 ...

  5. 《Java程序设计》第二周学习总结

    20145224陈颢文<Java程序设计>第二周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long ...

  6. 20155304田宜楠 2006-2007-2 《Java程序设计》第二周学习总结

    20155304田宜楠 2006-2007-2 <Java程序设计>第二周学习总结 教材学习内容总结 一.类型与变量 1.类型 整数: 可细分为为short整数(占2字节),int整数(占 ...

  7. 2017面向对象程序设计(Java)第二周学习总结

    2017面向对象程序设计(Java)第二周学习总结 直系学妹学弟们好!额...不要问我为什么把学妹放前面,我也不知道!我只是你们和蔼可亲的学长一枚而已.也不要问为什么是第二周学习总结而不是第一周,因为 ...

  8. 201521123038 《Java程序设计》 第二周学习总结

    201521123038 <Java程序设计> 第二周学习总结 1.本章学习总结 学会在Java程序中使用函数,使程序层次更清晰 使用StringBuilder编写代码,减少内存空间的占用 ...

  9. 201521123093 java 第二周学习总结

    201521123093 <java程序设计> 第二周学习总结 一.第二周学习总结 答:(1)关于进一步使用码云管理代码,本周才真正学会了如何将Eclipse里的代码上传到码云中,并且能够 ...

随机推荐

  1. JavaScript高级程序设计之函数性能

    setTimeout 比 setInterval 性能更好 // 取代setInterval setTimeout(function self () { // code goes here setTi ...

  2. IOS中GPS定位偏移纠正(适用于Google地图)

    在这个神奇的国度里,我们总得学习一些有中国特色的东东,例如“火星坐标”.也许有人还不知道这是什么玩意,我就简要介绍一下吧.      如果你有带GPS模块的智能手机,打开定位功能,然后访问Google ...

  3. python学习笔记1

    python3.3使用urllib2报错 no module named urllib2,原因是python3中将urllib2换成了request. 所以要使用import urllib.reque ...

  4. AUTH过程

    INITIALIZE UPDATE: 在安全通道的显式发起期间,INITIALIZEUPDATE命令用于在卡和主机之间传送卡和会话数据.这个命令开始一个安全通道会话的发起. CPURESET() // ...

  5. Careercup - Google面试题 - 6253551042953216

    2014-05-06 01:49 题目链接 原题: Modify the following code to add a row number for each line is printed pub ...

  6. Entity Framework走马观花之把握全局

    在深入学习某项技术之前,应该努力形成对此技术的总体印象,并了解其基本原理,本文的目的就在于此. 一.理解EF数据模型 EF本质上是一个ORM框架,它需要把对象映射到底层数据库中的表,为此,它使用了三个 ...

  7. Memcached常用命令及使用说明

    一.存储命令 存储命令的格式: 1 2 <command name> <key> <flags> <exptime> <bytes> < ...

  8. boost之bind,function,signal总结

    boost里的bind,function,signal三个组件都是对用函数做参数(其他算法也用函数做参数),对函数的某一项进行操作. bind主要是对函数参数的作用. function主要是对函数地址 ...

  9. 【BZOJ】【2500】幸福的道路

    树形DP+单调队列优化DP 好题(也是神题……玛雅我实在是太弱了TAT,真是一个250) 完全是抄的zyf的……orz我还是退OI保平安吧 第一步对于每一天求出一个从第 i 个点出发走出去的最长链的长 ...

  10. 浅谈KL散度

    一.第一种理解 相对熵(relative entropy)又称为KL散度(Kullback–Leibler divergence,简称KLD),信息散度(information divergence) ...