一、使用模板方法设计模式简化开发

模板方法设计模式,执行一个程序有很多步骤,将每次都要执行的共有的提取出来放到一个抽象父类中,变化的部分通过让子类传递参数过来或将这部分抽象为抽象方法让子类通过继承的方式去实现.

我们的UserDaoJdbcImpl 有大量重复的代码,使用模板设计模式重构如下:

//定义抽象类,提取公共部分
public abstract class AbstractDao { // 查
public Object find(String sql, Object[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = ; i < args.length; i++) {
ps.setObject(i + , args[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
obj = rowMapper(rs);
}
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
return obj;
} // 将变化的部分,让子类去实现
public abstract Object rowMapper(ResultSet rs);
  
   //增删改
public void update(String sql, Object[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = ; i < args.length; i++) {
ps.setObject(i + , args[i]);
}
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
}
public class UserDaoImpl extends AbstractDao implements UserDao{

        public void update(User user) {
String sql = "update user set name=?, birthday=?, money=? where id=? ";
Object[] args = new Object[] { user.getName(), user.getBirthday(),
user.getMoney(), user.getId() };
super.update(sql, args);
} public User findUser(String loginName, String password) {
String sql = "select id, name, money, birthday from user where name=?";
Object[] args = new Object[] { loginName };
Object user = super.find(sql, args);
return (User) user;
} public Object rowMapper(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;
}
}

这种方式有一个问题就是UserDaoImp类中只能有一个rowMapper方法,如果还有一个查询方法要映射,则无法实现只能在写一个类,相当不灵活.既然变化的sql和参数可以从子类传递那么映射也可以传递,结合策略模式重构上面代码如下.

二、使用策略模式对模板方法设计模式进行改进

//没有抽象方法了,也没有必要定义为抽象类
public class MyTemplete {
//RowMapper定义为一个接口,可以有不同的实现,相当于不同的策略
public Object find(String sql,Object[] args,RowMapper rowMapper) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=;i<args.length;i++){
ps.setObject(i+, args[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
obj = rowMapper.rowMapper(rs);
}
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
return obj;
}
} public interface RowMapper{
Object rowMapper(ResultSet rs)throws SQLException;
}
public class UserDaoImpl  implements UserDao{
MyTemplete myTemplete = new MyTemplete(); //定义一个模板
public User findUser(String loginName, String password) {
String sql = "select id, name, money, birthday from user where name=?";
Object[] args = new Object[] { loginName };
Object user = myTemplete.find(sql, args,new RowMapper(){
@Override
public Object rowMapper(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;
}
});
return (User) user;
}
}

三、Apache—DBUtils框架

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能.因此dbutils成为很多不喜欢hibernate的公司的首选.
//使用dbutils完成数据库的crud
public class Demo1 { @Test
public void insert() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
Object params[] = {,"bbb","","aa@sina.com",new Date()};
runner.update(sql, params);
} @Test
public void update() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "update users set email=? where id=?";
Object params[] = {"aaaaaa@sina.com",};
runner.update(sql, params);
} @Test
public void delete() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "delete from users where id=?";
runner.update(sql, );
} @Test
public void find() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from users where id=?";
User user = (User) runner.query(sql, , new BeanHandler(User.class));
System.out.println(user.getEmail());
} @Test
public void getAll() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from users";
List list = (List) runner.query(sql, new BeanListHandler(User.class));
System.out.println(list);
} @Test
public void batch() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
Object params[][] = new Object[][];
for(int i=;i<params.length;i++){ //
params[i] = new Object[]{i+,"aa"+i,"",i + "@sina.com",new Date()};
}
runner.batch(sql, params);
}
}
//测试dbutils的各个结果集处理器,只是一部分
public class Demo2 { @Test
public void test1() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from users";
Object result[] = (Object[]) runner.query(sql, new ArrayHandler());
System.out.println(result[]);
System.out.println(result[]);
} @Test
public void test2() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from users";
List list = (List) runner.query(sql, new ArrayListHandler());
System.out.println(list);
} @Test
public void test3() throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from users";
List list = (List) runner.query(sql, new ColumnListHandler1("name"));
System.out.println(list);
}
}

ps:采用DBUtils后,jdbcUtils工具类中获取连接和释放连接的方法也不需要了,只需提供获取数据源的方法(如果进行做事务处理还需要).

四、jdbc应用事务管理

1、使用如上所示的jdbcUtils时,我们无需管理连接的获取和关闭,在每次进行完一条sql的执行后连接会自动归还连接池.但是要进行事务处理的话就要手动获取和关闭连接,因为同一个事务必须使用同一个数据库连接,转账案例dao层代码如下:

//从a--->b帐户转100元
public void transfer() throws SQLException{
Connection conn = null;
try{
conn = JdbcUtils.getConnection();//连接池中获取连接
conn.setAutoCommit(false); QueryRunner runner = new QueryRunner();
String sql1 = "update account set money=money-100 where name='aaa'";
runner.update(conn,sql1); String sql2 = "update account set money=money+100 where name='bbb'";
runner.update(conn,sql2); conn.commit();
}finally{
if(conn!=null){
conn.close();//连接放回连接池
}
}
}

ps:dao层做事务管理的话就把业务逻辑放在了dao层,违背三层架构设计思想,dao层只能做简单的增删改查,于是要将事务管理放在service层,代码如下:

public void transfer1(int sourceid,int targetid,double money) throws SQLException{

    Connection conn = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false); AccountDao dao = new AccountDao(conn); //把当前conn传递给dao
Account a = dao.find(sourceid); //select
Account b = dao.find(targetid); //select
a.setMoney(a.getMoney()-money);
b.setMoney(b.getMoney()+money);
dao.update(a); //update
dao.update(b);//update conn.commit();
}finally{
if(conn!=null) conn.close();
}
}

2、更优雅的做法,使用ThreadLocal(*重要),ThreadLocal可以实现线程内的数据共享,ThreadLocal其实是一个大大的Map集合,set方法就是往map集合中存数据,map的key是当前线程的名称,ThreadLocal API如下,只有三个方法:

T get()      Returns the value in the current thread's copy of this thread-local variable.
void remove() Removes the current thread's value for this thread-local variable.
void set(T value) Sets the current thread's copy of this thread-local variable to the specified value.

使用ThreadLocal后代码如下:

//QueryRunner 的各种更新和查询可以接收一个数据库连接,简直就是为事务处理设置的,不处理事务没必要传连接
public class AccountDao { private Connection conn; public AccountDao(Connection conn) {
this.conn = conn;
} public void update(Account a) {
try {
QueryRunner runner = new QueryRunner();
String sql = "update account set money=? where id=?";
Object params[] = { a.getMoney(), a.getId() };
runner.update(JdbcUtils.getConnection(), sql, params);
} catch (Exception e) {
throw new RuntimeException(e);
}
} public Account find(int id) {
try {
QueryRunner runner = new QueryRunner();
String sql = "select * from account where id=?";
return (Account) runner.query(JdbcUtils.getConnection(), sql, id,
new BeanHandler(Account.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
//用上ThreadLocal的事务管理
public void transfer2(int sourceid,int targetid,double money) throws SQLException{
try{
JdbcUtils.startTransaction();
AccountDao dao = new AccountDao();
Account a = dao.find(sourceid); //select
Account b = dao.find(targetid); //select
a.setMoney(a.getMoney()-money);
b.setMoney(b.getMoney()+money);
dao.update(a); //update
dao.update(b);//update
JdbcUtils.commitTransaction();
}finally{
JdbcUtils.closeConnection();
}
}
public class JdbcUtils {
private static DataSource ds;
static {
try {
Properties prop = new Properties();
InputStream in = JdbcUtils.class.getClassLoader()
.getResourceAsStream("dbcpconfig.properties");
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} public static DataSource getDataSource() {
return ds;
} // 不进行事务处理的话上面的代码就够了,下面用来进行事务处理
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); public static Connection getConnection() throws SQLException {
try {
// 得到当前线程上绑定的连接
Connection conn = tl.get();
if (conn == null) { // 代表线程上没有绑定连接
conn = ds.getConnection();
tl.set(conn);
}
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
} public static void startTransaction() {
try {
// 得到当前线程上绑定连接开启事务
Connection conn = tl.get();
if (conn == null) { // 代表线程上没有绑定连接
conn = ds.getConnection();
tl.set(conn);
}
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
} public static void commitTransaction() {
try {
Connection conn = tl.get();
if (conn != null) {
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} public static void closeConnection() {
try {
Connection conn = tl.get();
if (conn != null) {
conn.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
tl.remove();// 千万注意,解除当前线程上绑定的链接(从threadlocal容器中移除对应当前线程的链接)
}
}
}

ps:ThreadLocal用处很多,很多框架中使用它存储对象,比如现在servlet中使用外部定义的对象可以有三种方式,通过参数传递给它,通过JNDI容器,通过ThreadLocal.

五、servlet层事务管理

1、如果service层事务管理不能满足需求,需要在servlet层进行事务处理,使用如上代码也可实现,将JdbcUtils.startTransaction(); JdbcUtils.commitTransaction(); JdbcUtils.closeConnection();放在servlet中即可.

2、如果单servlet也不能满足需求,比如实现servlet转发后还需要事务处理,则要可以使用Filter,在Filter中给ThreadLocal绑定连接.

ps:也就是实现jdbc事务必须使用同一个数据库连接,只需要将ThreadLocal与数据库连接的绑定往上层提即可.

***事务的处理使用springJDBC好像比较方便,以后再看.

六、jdbc多表操作

1、员工和部门是一对多的关系,老师和学生是多对多的关系,设计表的时候一定要注意.如下老师学生代码:

public void add(Teacher t) throws SQLException {

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

    //1`.取出老师存老师表
String sql = "insert into teacher(id,name,salary) values(?,?,?)";
Object params[] = {t.getId(),t.getName(),t.getSalary()};
runner.update(sql, params); //2.取出老师所有学生的数据,存学生表
Set<Student> set = t.getStudents();
for(Student s : set){
sql = "insert into student(id,name) values(?,?)";
params = new Object[]{s.getId(),s.getName()};
runner.update(sql, params); //3.更新中间表,说明老师和学生的关系
sql = "insert into teacher_student(teacher_id,student_id) values(?,?)";
params = new Object[]{t.getId(),s.getId()};
runner.update(sql, params);
}
}

推理:这段代码必须要加事务处理,那么在dao层中又加入了逻辑代码,所以应该将这段代码在dao层中分开写,在service层中进行事务处理.

七、springJDBC使用

public class JdbcTemplateTest {

    static JdbcTemplate jdbc = new JdbcTemplate(JdbcUtils.getDataSource());
//查询
static User findUser(String name) {
String sql = "select id, name, money, birthday from user where name=?";
Object[] args = new Object[] { name };
Object user = jdbc.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"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
});
return (User) user;
}
//BeanPropertyRowMapper利用原数据和反射直接封装到User中
static User findUser1(String name) {
String sql = "select id, name, money, birthday from user where name=?";
Object[] args = new Object[] { name };
Object user = jdbc.queryForObject(sql, args, new BeanPropertyRowMapper(
User.class));
return (User) user;
}
//特殊的跟新要拿到插入的组建,其它正常更新略
static int addUser(final User user) {
jdbc.execute(new ConnectionCallback() {
public Object doInConnection(Connection con) throws SQLException,
DataAccessException {
String sql = "insert into user(name,birthday, money) values (?,?,?) ";
PreparedStatement ps = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getName());
ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
ps.setFloat(3, user.getMoney());
ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys();
if (rs.next())
user.setId(rs.getInt(1));
return null;
}
});
return 0;
}
}

ps:以后补充springJDBC.

javaEE(13)_jdbc框架的更多相关文章

  1. javaweb基础(40)_jdbc框架

    一.元数据介绍 元数据指的是"数据库"."表"."列"的定义信息. 1.1.DataBaseMetaData元数据 Connection.g ...

  2. 6月13 ThinkPHP框架基础

    ThinkPHP 一.php框架基础介绍 真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维 ...

  3. python基础-第十三篇-13.1web框架本质

    基础与概念 众所周知,对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端 web框架分两类:一类是包括socket和业务逻辑(tornado),另一 ...

  4. JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(12):XML配置自动扫描包,自动加载*.properties文件

    一.XML和注解组合使用 前几篇的测试案例都是在Java类中配置,现在换一种使用方式,在XML中配置,使Spring IoC容器在启动之后自动去扫描配置的包路径,扫描加载指定路径下的propertie ...

  5. python基础-第十三篇-13.2Web框架之Tornado

    Tornado是非阻塞异步web frame,而且速度相当快,得力于其非阻塞的方式和对epoll的运用 Tornado每秒可以处理数以千计的链接,所以它可以有效的处理C10K问题 下载安装 pip3 ...

  6. JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(2):SSM+Redis概念理解

    一.SSM+Redis的结构图 在Java互联网中,以Spring+SpringMVC+MyBatis(SSM)作为主流框架,SSM+Redis的结构图如下: 二.下面介绍它们各自承担的功能: 1.S ...

  7. JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(1):Mybatis和Hibernate概念理解

    一.关键字说明: oop:面向对象 aop:面向切面 ioc:控制反转 orm:对象关系映射 pojo:数据库表映射的java实体类 二.常识说明:1.hibernate和mybatis都属于持久层. ...

  8. Apache activiti5.13工作流框架的表结构详解

    1.结构设计 1.1.    逻辑结构设计 Activiti使用到的表都是ACT_开头的. ACT_RE_*: ’RE’表示repository(存储),RepositoryService接口所操作的 ...

  9. [Swift通天遁地]八、媒体与动画-(13)CoreText框架实现图文混排

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

随机推荐

  1. 51nod1393

    思路:一个位num0-num1值=某位num0-num1值相等就代表这段区间内01数字相等,然后还要判断当前位置num0==num1这个情况 #include <bits/stdc++.h> ...

  2. github新手使用

    1.首先要先在github的官网注册一个属于自己的账号.https://github.com/ 2.注册完成后需要一些简单的设置,先创建一个属于自己的仓库,repository 3.创建仓库 4.如何 ...

  3. python+unittest 搭建简易的接口测试框架

    主要介绍如何使用python+unittest快速搭建一个接口测试的框架 1.安装python  unittest 2.新建一个python项目ApiTest 在setUp和setDown里设置一些需 ...

  4. NPOI用WorkbookFactory读写 2007以上格式文件(xlsx)

    //我用的最新的2.2.1版本 //第一步:引用DLL,5个全导入,包括ICSHARP.ZIP,是个开源压缩工具包.XLSX是压缩格式,需要它来解压 //第二部: using NPOI.SS.User ...

  5. Git - .gitignore怎么忽略已经被版本控制的文件

    问题 如果某个文件已经存在于远程仓库了,也就是说某个文件已经被版本控制了,如果将该文件添加到.gitignore中,是无法生效的.因为.gitignore是用来控制尚未被纳入版本控制的文件,如果文件已 ...

  6. Serervlet | 慕课课程实战 | 编写登录逻辑

    Users.java package com.po; public class Users { private String username; private String password; pu ...

  7. keil_rtx调试技巧

    超级循环结构的程序调试一般依赖于断点,单步,查看变量和内存变量(keil中的Memory Window 或者 Watch window):而带微操作系统的程序由于加了这个中间层调试方法可能传统的有些区 ...

  8. nodejs express session用法(含保存到redis)

    普通用法: var express = require('express'); var session = require('express-session'); var app = express( ...

  9. 前端之css(宽高)设置小技巧

    一.css宽高自适应: 1.宽度自适应: 元素宽度设为100%(块状元素的默认宽度为100%) 注:应用在通栏效果中 2.高度自适应: height:auto;或者不设置高度 3.最小,最大高度,最小 ...

  10. JS中的回收机制

    js的设计者为了让没有必要的变量保存在内存中,(我们写的任何变量都是需要内存空间的),什么叫没有必要的变量?也就是说你不在需要这个变量的时候它就会被销毁?那么你肯定会问js怎么知道那些变量是我们不需要 ...