JDBC第二次学习
脑子太笨,必须得记录下来一些文字,方便回来查询。
这是我的第二次学习JDBC的笔记,看的是传智播客——李勇老师的JDBC系列,已看到第23集。
分析在实际项目中该如何应用JDBC
一个简单用户相关的数据访问层
- J2EE三层架构简介:表示层 、业务逻辑层、数据访问层,三层之间用接口隔离。
定义domain对象User,定义存取用户的接口。
用JDBC实现接口。
- 用配置文件(properties)和反射实现与具体类的耦合。
用图来表示:
在真实的项目开发中,我们能不能返回ResultSet对象呢? 如下例代码:
- static ResultSet read() throws SQLException {
- Connection conn = null;
- Statement st = null;
- ResultSet rs = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- st = conn.createStatement();
- //4.执行语句
- rs = st.executeQuery("select id,name,birthday,money from user");
- //5.处理结果
- while(rs.next()) {
- System.out.println(rs.getObject("id")+"\t"+rs.getObject("name")+
- "\t"+rs.getObject("birthday")+"\t"+rs.getObject("money"));
- }
- return rs;//连接关闭,Statement、ResultSet都失效了。
- } finally {
- JdbcUtils.free(rs, st, conn);
- }
- }
在main()方法中调用:
- ResultSet rs = read();
这个其实是不可以的,当Connection关闭之后,ResultSet中的数据你就拿不到了(Statement、ResultSet都失效了)。如果我们要进行传值需要定义个domain对象。
我们重点关注数据访问层的代码如何书写
1、定义domain对象User以及存取用户的接口:
- import cn.itcast.jdbc.domain.User;
- public interface UserDao {
- public void addUser(User user);
- public User getUser(int userId);
- public User findUser(String loginName, String password);
- public void update(User user);
- public void delete(User user);
- }
- import java.util.Date;
- public class User {
- private int id;
- private String name;
- private Date birthday;
- private float money;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Date getBirthday() {
- return birthday;
- }
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- public float getMoney() {
- return money;
- }
- public void setMoney(float money) {
- this.money = money;
- }
- }
2、用JDBC实现接口。在实现的过程中,准确一点说会出现SQLException。关于这个异常的处理,很多工作2年的程序员都不知道怎么样处理,所以对于此异常,定当重视,通常我们结合Service层讲解DAO层的异常处理的方式。
就拿addUser(user)方法来说,中间是有可能出现SQLException的,在处理异常的时候,不能catch住异常之后什么都不做,最起码要打印一下堆栈,最好的方式是将这个异常转化为RuntimeException抛出。做法如下:
建立一个DaoException继承RuntimeException,如下:
- public class DaoException extends RuntimeException {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- public DaoException() {
- // TODO Auto-generated constructor stub
- }
- public DaoException(String message) {
- super(message);
- // TODO Auto-generated constructor stub
- }
- public DaoException(Throwable cause) {
- super(cause);
- // TODO Auto-generated constructor stub
- }
- public DaoException(String message, Throwable cause) {
- super(message, cause);
- // TODO Auto-generated constructor stub
- }
- public DaoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- // TODO Auto-generated constructor stub
- }
- }
之后JDBC实现接口的过程中,就抛出DaoException异常,如下(代码太多,折叠之):
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import cn.itcast.jdbc.JdbcUtils;
- import cn.itcast.jdbc.dao.DaoException;
- import cn.itcast.jdbc.dao.UserDao;
- import cn.itcast.jdbc.domain.User;
- public class UserDaoJdbcImpl implements UserDao {
- @Override
- public void addUser(User user) {
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- String sql = "insert into user (name,birthday,money) values (?,?,?)";
- ps = conn.prepareStatement(sql);
- ps.setString(1, user.getName());
- ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
- ps.setFloat(3, user.getMoney());
- //4.执行语句
- ps.executeUpdate();
- } catch(SQLException e) {
- throw new DaoException(e.getMessage(), e);
- } finally {
- JdbcUtils.free(rs, ps, conn);
- }
- }
- @Override
- public User getUser(int userId) {
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- User user = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- String sql = "select id,name,birthday,money from user where id = ?";
- ps = conn.prepareStatement(sql);
- ps.setInt(1, userId);
- //4.执行语句
- rs = ps.executeQuery();
- //5.处理结果
- while(rs.next()) {
- /*
- * 优化①处
- */
- user = mappingUser(rs);
- }
- } catch(SQLException e) {
- throw new DaoException(e.getMessage(), e);
- } finally {
- JdbcUtils.free(rs, ps, conn);
- }
- return user;
- }
- @Override
- public User findUser(String loginName, String password) {
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- User user = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- String sql = "select id,name,birthday,money from user where name = ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, loginName);
- //4.执行语句
- rs = ps.executeQuery();
- //5.处理结果
- while(rs.next()) {
- /*
- * 优化①处
- */
- user = mappingUser(rs);
- }
- } catch(SQLException e) {
- throw new DaoException(e.getMessage(), e);
- } finally {
- JdbcUtils.free(rs, ps, conn);
- }
- return user;
- }
- private User mappingUser(ResultSet rs) throws SQLException {
- User user = new User();
- user.setId(rs.getInt("id"));
- user.setName(rs.getString("name"));
- user.setMoney(rs.getFloat("money"));
- user.setBirthday(rs.getDate("birthday"));
- return user;
- }
- @Override
- public void update(User user) {
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- String sql = "update user set name = ?,birthday = ?,money = ? where id = ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, user.getName());
- ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
- ps.setFloat(3, user.getMoney());
- ps.setInt(4, user.getId());
- //4.执行语句
- ps.executeUpdate();
- } catch(SQLException e) {
- throw new DaoException(e.getMessage(), e);
- } finally {
- JdbcUtils.free(rs, ps, conn);
- }
- }
- @Override
- public void delete(User user) {
- Connection conn = null;
- Statement st = null;
- ResultSet rs = null;
- try {
- //2.建立连接
- conn = JdbcUtils.getConnection();
- //3.创建语句
- st = conn.createStatement();
- //4.执行语句
- String sql = "delete from user where id = "+user.getId();
- st.executeUpdate(sql);
- } catch(SQLException e) {
- throw new DaoException(e.getMessage(), e);
- } finally {
- JdbcUtils.free(rs, st, conn);
- }
- }
- }
用配置文件(properties)和反射实现与具体类的耦合(?),此时就要用到工厂模式了。
DaoFactory类如下(视频说代码很经典,谁知道呢?):
- import java.io.InputStream;
- import java.util.Properties;
- public class DaoFactory {
- /*
- * 注意两个static属性之间的顺序
- * 如果搞反,那么属性userDao始终为空!
- */
- private static UserDao userDao = null;
- private static DaoFactory instance = new DaoFactory();
- private DaoFactory() {
- try {
- Properties prop = new Properties();
- /*
- * 方式一
- * 缺点:路径是写死的,如果我们的配置文件改变了地方,就找不到文件了 。
- */
- //FileInputStream fis = new FileInputStream("src/daoconfig.properties");
- /*
- * 方式二
- * 优点:用类加载器(马丹,涉及到反射技术,又是一个要补的地方)的方法
- * 来得到一个文件的输入流,只要这个文件在classpath路径下(其实编译的xxx.class文件和配置文件
- * 就在根目录下bin目录中,而次bin目录被自动加载到classpath变量值中(别问我why?我也不知道))就能找到。
- */
- InputStream fis = DaoFactory.class.getClassLoader()//类加载器(???)
- .getResourceAsStream("daoconfig.properties");//daoconfig.properties只要在classpath路径下,就可以加载进来,更加灵活
- prop.load(fis);
- String userDaoClass = prop.getProperty("userDaoClass");
- /*
- * 涉及到反射技术
- */
- Class clazz = Class.forName(userDaoClass);//将类加载到JVM
- userDao = (UserDao)clazz.newInstance();//实例化一个对象
- //System.out.println(userDao);
- } catch (Throwable e) {
- throw new ExceptionInInitializerError(e);
- }
- }
- public static DaoFactory getInstance() {
- return instance;
- }
- public UserDao getUserDao() {
- //System.out.println(userDao);
- return userDao;
- }
- }
整体的框架图应该如这幅模样:
JDBC事务处理
关于事务,我已经在《浅谈事务》一文中详细记录过,在此不赘述了。我们只关注JDBC是如何处理事务的。
示例1(用代码来体现):
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- public class TxIsolateTest {
- public static void main(String[] args) throws SQLException {
- test();
- }
- static void test() throws SQLException {
- Connection conn = null;
- Statement st = null;
- ResultSet rs = null;
- try {
- conn = JdbcUtils.getConnection();
- /*
- * 设置事务为手动提交,一说也叫打开事务。
- */
- conn.setAutoCommit(false);
- /*
- * 设置事务的隔离级别,这是一个超麻烦的问题,李勇老师也是敷衍过去!
- */
- conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
- String sql = "update user set money = money - 10 where id = 1";
- st = conn.createStatement();
- st.executeUpdate(sql);
- sql = "select money from user where id = 2";
- rs = st.executeQuery(sql);
- float money = 0.0f;
- if(rs.next()) {
- money = rs.getFloat("money");
- }
- if(money > 400)
- throw new RuntimeException("已经超过最大值!");
- sql = "update user set money = money + 10 where id = 2";
- st.executeUpdate(sql);
- /*
- * 提交事务
- */
- conn.commit();
- } catch (SQLException e) {
- if(conn != null)
- /*
- * 回滚事务,rollback()中不加任何参数,即为回滚所有操作
- */
- conn.rollback();
- throw e;
- } finally {
- JdbcUtils.free(rs, st, conn);
- }
- }
- }
示例2:
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Savepoint;
- import java.sql.Statement;
- public class SavePointTest {
- public static void main(String[] args) throws SQLException {
- test();
- }
- static void test() throws SQLException {
- Connection conn = null;
- Statement st = null;
- ResultSet rs = null;
- Savepoint sp = null;
- try {
- conn = JdbcUtils.getConnection();
- conn.setAutoCommit(false);
- st = conn.createStatement();
- String sql = "update user set money = money - 10 where id = 1";
- st.executeUpdate(sql);
- /*
- * 设置保存点
- */
- sp = conn.setSavepoint();
- sql = "update user set money = money - 10 where id = 3";
- st.executeUpdate(sql);
- sql = "select money from user where id = 2";
- rs = st.executeQuery(sql);
- float money = 0.0f;
- if(rs.next()) {
- money = rs.getFloat("money");
- }
- if(money > 300)
- throw new RuntimeException("已经超过最大值!");
- sql = "update user set money = money + 10 where id = 2";
- st.executeUpdate(sql);
- conn.commit();
- } catch (RuntimeException e) {
- if(conn != null && sp != null) {
- conn.rollback(sp);//回滚到保存点上去,再提交,这样保证到保存点之前那些操作都是有效的(只回滚一部分操作)
- conn.commit();
- }
- throw e;
- } catch (SQLException e) {
- if(conn != null)
- conn.rollback();
- throw e;
- } finally {
- JdbcUtils.free(rs, st, conn);
- }
- }
- }
JTA分布式事务的简要介绍(李勇老师也是敷衍过去了)
跨越多个数据源的事务,使用JTA容器(是啥,不知道?)实现事务。
分成两阶段提交。
代码格式总是如此:
- javax.transaction.UserTransaction tx = (UserTransaction)ctx.lookup(“jndi(?)Name");
- tx.begin();
- //connection1 connection2 (可能来自不同的数据库)…
- tx.commit();//tx.rollback();
JDBC第二次学习的更多相关文章
- 20145213《Java程序设计》第二周学习总结
20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...
- 20145330孙文馨 《Java程序设计》第二周学习总结
20145330孙文馨第二周学习总结 第二周相比于第一周对java语言有了深一点的了解,也意识到多敲代码才是学习计算机语言的最好方法. 教材内容总结 类型.变量与运算符 *基本类型 整数(short. ...
- 20145337 《Java程序设计》第二周学习总结
20145337 <Java程序设计>第二周学习总结 教材学习内容总结 Java可分基本类型与类类型: 基本类型分整数(short.int.long).字节(byte).浮点数(float ...
- 20135328信息安全系统设计基础第二周学习总结(vim、gcc、gdb)
第三周学习笔记 学习计时:共8小时 读书:1 代码:5 作业:1 博客:7 一.学习目标 熟悉Linux系统下的开发环境 熟悉vi的基本操作 熟悉gcc编译器的基本原理 熟练使用gcc编译器的常用选项 ...
- 《Java程序设计》第二周学习总结
20145224陈颢文<Java程序设计>第二周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long ...
- 20155304田宜楠 2006-2007-2 《Java程序设计》第二周学习总结
20155304田宜楠 2006-2007-2 <Java程序设计>第二周学习总结 教材学习内容总结 一.类型与变量 1.类型 整数: 可细分为为short整数(占2字节),int整数(占 ...
- 2017面向对象程序设计(Java)第二周学习总结
2017面向对象程序设计(Java)第二周学习总结 直系学妹学弟们好!额...不要问我为什么把学妹放前面,我也不知道!我只是你们和蔼可亲的学长一枚而已.也不要问为什么是第二周学习总结而不是第一周,因为 ...
- 201521123038 《Java程序设计》 第二周学习总结
201521123038 <Java程序设计> 第二周学习总结 1.本章学习总结 学会在Java程序中使用函数,使程序层次更清晰 使用StringBuilder编写代码,减少内存空间的占用 ...
- 201521123093 java 第二周学习总结
201521123093 <java程序设计> 第二周学习总结 一.第二周学习总结 答:(1)关于进一步使用码云管理代码,本周才真正学会了如何将Eclipse里的代码上传到码云中,并且能够 ...
随机推荐
- JavaScript高级程序设计之函数性能
setTimeout 比 setInterval 性能更好 // 取代setInterval setTimeout(function self () { // code goes here setTi ...
- IOS中GPS定位偏移纠正(适用于Google地图)
在这个神奇的国度里,我们总得学习一些有中国特色的东东,例如“火星坐标”.也许有人还不知道这是什么玩意,我就简要介绍一下吧. 如果你有带GPS模块的智能手机,打开定位功能,然后访问Google ...
- python学习笔记1
python3.3使用urllib2报错 no module named urllib2,原因是python3中将urllib2换成了request. 所以要使用import urllib.reque ...
- AUTH过程
INITIALIZE UPDATE: 在安全通道的显式发起期间,INITIALIZEUPDATE命令用于在卡和主机之间传送卡和会话数据.这个命令开始一个安全通道会话的发起. CPURESET() // ...
- Careercup - Google面试题 - 6253551042953216
2014-05-06 01:49 题目链接 原题: Modify the following code to add a row number for each line is printed pub ...
- Entity Framework走马观花之把握全局
在深入学习某项技术之前,应该努力形成对此技术的总体印象,并了解其基本原理,本文的目的就在于此. 一.理解EF数据模型 EF本质上是一个ORM框架,它需要把对象映射到底层数据库中的表,为此,它使用了三个 ...
- Memcached常用命令及使用说明
一.存储命令 存储命令的格式: 1 2 <command name> <key> <flags> <exptime> <bytes> < ...
- boost之bind,function,signal总结
boost里的bind,function,signal三个组件都是对用函数做参数(其他算法也用函数做参数),对函数的某一项进行操作. bind主要是对函数参数的作用. function主要是对函数地址 ...
- 【BZOJ】【2500】幸福的道路
树形DP+单调队列优化DP 好题(也是神题……玛雅我实在是太弱了TAT,真是一个250) 完全是抄的zyf的……orz我还是退OI保平安吧 第一步对于每一天求出一个从第 i 个点出发走出去的最长链的长 ...
- 浅谈KL散度
一.第一种理解 相对熵(relative entropy)又称为KL散度(Kullback–Leibler divergence,简称KLD),信息散度(information divergence) ...