• 选学校
  • 高中毕后选择什么学校好
  • 启蒙星
  • 北大青鸟课程介绍
  • 北大青鸟助你走上高级软件工程师修炼之路
  • 转行不是梦,学北大青鸟IT培训
  • 北大青鸟辉煌12年
  • 株洲北大青鸟校区升级

您现在的位置:株洲北大青鸟 >> >> 师资力量>> 技术天地

师资力量
开班信息
输入姓名手机号码预约试听课程
姓  名:*
手机号:*
创业摇篮班
开班日期:10月15日
招生人数:25
就业直通班
开班日期:10月31日
招生人数:25
就业精英班
开班日期:9月13日
招生人数:已满
技术天地
  • 使用代理模式处理JDBC事务
  • 发表日期:2011/5/14 14:36:50 阅读数:1036  
  •  
  • 使用代理模式处理JDBC事务

    --李赞红

     JDBC默认情况下是手动提交事务的,但对于稍微复杂的业务必须定义事务边界,统一提交,这样才能保证业务的完整性。但如何将事务与业务有机整合起来?普通的做法是在业务方法中定义事务边界,但这样做的后果是代码冗余,失去代码的艺术性。在此,我提供一种方法,即使用动态代理模式将业务与事务有机结合,代码上完全分离,但又能达到既定的效果。


    首先,定义DBHelper类实现数据库访问的通用操作:
    public class DBHelper {
    private Connection conn;
    private PreparedStatement stmt;
    private ResultSet rs;
    private static Logger log = Logger.getLogger(DBHelper.class);
    public static DBHelper getInstance(){
    Connection conn = TransactionUtil.getConnection();
    DBHelper dbHelper = new DBHelper();
    dbHelper.setConn(conn);
    return dbHelper;
    }
    /**
    * 只返回一个值的查询,如select count(*) from dept;
    * @param sql
    * @param params
    * @return
    */
    public Object getOne(String sql, Object...params){
    log.debug(sql);
    try {
    stmt = conn.prepareStatement(sql);
    this.bindParameters(params);
    ResultSet rs = stmt.executeQuery();
    if(rs.next())
    return rs.getObject(1);
    else
    return null;
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally{
    this.release();
    }
    return null;
    }
    /**
    * 查询
    * @param sql sql语句
    * @param params 参数列表
    * @return
    */
    public List<Map<String, Object>> query(String sql, Object...params){
    log.debug(sql);
    try {
    Map<String, Object> map = null;
    List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
    stmt = conn.prepareStatement(sql);
    this.bindParameters(params);
    ResultSet rs = stmt.executeQuery();
    ResultSetMetaData meta = rs.getMetaData();
    if(rs != null){
    while(rs.next()){
    map = new HashMap<String, Object>();
    for(int i = 0; i < meta.getColumnCount(); i ++){
    map.put(meta.getColumnName(i + 1), 
    rs.getObject(i + 1));
    }
    list.add(map);
    }
    }
    return list;
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally{
    this.release();
    }
    return null;
    }
    /**
    * 执行insert, update 和 delete语句
    * @param sql sql语句
    * @param params 参数列表,必须和sql语句中的?数量一致
    * @return
    */
    public int update(String sql, Object...params){
    log.debug(sql);
    try {
    if(this.conn != null){
    stmt = conn.prepareStatement(sql);
    bindParameters(params);
    return stmt.executeUpdate();
    }
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally{
    this.release();
    }
    return 0;
    }

    /**
    * 绑定参数
    * @param params
    * @throws SQLException
    */
    private void bindParameters(Object... params) throws SQLException {
    if(params != null){
    for(int i = 0; i < params.length; i ++){
    stmt.setObject(i + 1, params[i]);
    }
    }
    }
    /**
    * 释放资源
    */
    public void release(){
    try {
    if(rs != null){
    rs.close();
    }
    if(stmt != null){
    stmt.close();
    }
    } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    /**
    * 获取连接对象
    * @return
    */
    public Connection getConn() {
    return conn;
    }
    public void setConn(Connection conn) {
    this.conn = conn;
    }
    /**
    * 创建连接对象
    * @throws Exception 
    */
    public Connection createConnection() throws Exception{
    Class.forName("com.mysql.jdbc.Driver");
    conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/java_mail?useUnicode=true&amp;characterEncoding=UTF-8", "root", "110130ookdbdev");
    return conn;
    }
    }

    第二步,定义ConnectionPoolBean获取连接对象,该类应该设计成接口,提供更好的扩展性能,本例为简单起见,直接写成普通类。
    package com.ookang.jdbc;

    import java.sql.Connection;

    public class ConnectionPoolBean {
    public static Connection getConnection(){
    try {
    return new DBHelper().createConnection();
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    return null;
    }
    }

    第三步:创建Transaction类描述事务,支持嵌套事务
    package com.ookang.jdbc;

    import java.sql.Connection;

    public class Transaction {
    // 数据库连接对象
    private Connection connection;
    // 事务次数
    private int transCount;
    // 提交次数
    private int commitCount;
    // 事务嵌套层次
    private int transDeep;

    int getCommitCount() {
    return commitCount;
    }

    void setCommitCount(int commitCount) {
    this.commitCount = commitCount;
    }

    Connection getConnection() {
    return connection;
    }

    void setConnection(Connection conn) {
    this.connection = conn;
    }

    public int getTransCount() {
    return transCount;
    }

    void setTransCount(int transCount) {
    this.transCount = transCount;
    }

    int getTransDeep() {
    return transDeep;
    }

    void setTransDeep(int transDeep) {
    this.transDeep = transDeep;
    }

    /**
    * 判断事务是否完全提交。 通过提交次数和事务次数来判断事务是否完全提交。
    * @return
    */
    boolean hasFullExecute() {
    return commitCount + 1 == transCount;
    }
    }

    第四步:定义TransactionUtil 类实现事务处理
    package com.ookang.jdbc;

    import java.sql.Connection;
    import java.sql.SQLException;

    import org.apache.log4j.Logger;


    public class TransactionUtil {
    private final static ThreadLocal local = new ThreadLocal();
    private static Logger log = Logger.getLogger(TransactionUtil.class);

    /**
    * 开启事务
    */
    public static void startTransaction() throws ServiceException {
    Transaction tran = (Transaction) local.get();
    // 判断此事务是否属于一个顶层事务。
    if (tran == null) {
    tran = new Transaction();
    // 设置本地线程的connection
    Connection con = ConnectionPoolBean.getConnection();
    try {
    con.setAutoCommit(false);
    } catch (SQLException e) {
    e.printStackTrace();
    throw new ServiceException(e, "开启事务失败!");
    }
    tran.setConnection(con);
    tran.setCommitCount(0);
    tran.setTransCount(1);
    tran.setTransDeep(1);

    local.set(tran);
    } else {
    // 事务已经开启,将嵌套层次深度加一,将事务次数加一
    tran.setTransCount(tran.getTransCount() + 1);
    tran.setTransDeep(tran.getTransDeep() + 1);
    }
    }

    /**
    * 提交事务
    */
    public static void commitTransaction() throws ServiceException {
    Transaction tran = (Transaction) local.get();
    // 如果事务属于嵌套,则不提交数据,直接将层次数减一。
    if (tran.getTransDeep() > 1) {
    tran.setTransDeep(tran.getTransDeep() - 1);
    tran.setCommitCount(tran.getCommitCount() + 1);
    return;
    }

    Connection con = tran.getConnection();
    try {
    if (tran.hasFullExecute()) {
    con.commit();
    }
    } catch (SQLException e) {
    log.error(e);
    throw new ServiceException(e, "提交事务失败!");
    }
    }

    /**
    * 结束事务
    */
    public static void endTransaction() throws ServiceException {
    Transaction tran = (Transaction) local.get();
    // 如果事务属于嵌套,则不关闭连接,直接将层次数减一。
    if (tran.getTransDeep() > 1) {
    tran.setTransDeep(tran.getTransDeep() - 1);
    return;
    }

    // 当前事务已经结束,清空ThreadLocal变量,防止下一次操作拿到已经关闭的Connection对象。
    local.set(null);
    Connection con = tran.getConnection();
    try {
    if (!tran.hasFullExecute()) {
    con.rollback();
    }
    } catch (SQLException e) {
    log.error(e);
    throw new ServiceException(e, "事务回滚失败!");
    } finally {
    try {
    con.close();
    } catch (SQLException se) {
    log.error(se);
    throw new ServiceException(se, "关闭事务失败!");
    }
    }

    }

    /**
    * 获取当前事务的数据库连接。
    * @return
    */
    public static Connection getConnection() {
    Transaction tran = (Transaction) local.get();
    Connection con = tran.getConnection();
    if (con == null) {
    con = ConnectionPoolBean.getConnection();
    }
    return con;
    }

    }

    第五步:创建ApplyTransaction类用于事务整合(动态代理,传说中的AOP原型)
    package com.aptech.proxy;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    import org.apache.log4j.Logger;

    import com.ookang.jdbc.ServiceException;
    import com.ookang.jdbc.TransactionUtil;


    /**
     * 
     * @author LiZanhong
     * 
     */
    public class ApplyTransaction implements InvocationHandler {
    private Object service; 
    private static Logger log = Logger.getLogger(ApplyTransaction.class);

    public ApplyTransaction(Object service) {
    super();
    this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    TransactionUtil.startTransaction();
    log.debug("begin transaction.....");
    try {
    TransactionUtil.getConnection();
    Object o = method.invoke(service, args);
    TransactionUtil.commitTransaction();
    log.debug("commit transaction.....");
    return o;
    } catch (Exception e) {
    e.printStackTrace();
    throw new ServiceException(e);
    } finally {
    TransactionUtil.endTransaction();
    log.debug("end transaction.....");
    }
    }

    }


    第六步:获取业务代理对象
    package com.aptech.proxy;

    import java.lang.reflect.Proxy;

    /**
     * 
     * @author LiZanhong
     *
     */
    public class ProxyFactory {
    /**
    *
    * @param service
    * @return
    */
    public static Object getProxyInstance(Object service){
    return Proxy.newProxyInstance(
    service.getClass().getClassLoader(), 
    service.getClass().getInterfaces(), 
    new ApplyTransaction(service)); 
    }
    }


    第七步:定义业务类EmailUserServiceImpl ,该类完成邮箱用户的的基本操作,可以看出,没有与事务有关的任务代码。
    package com.ookang.service;

    import java.security.NoSuchAlgorithmException;
    import java.sql.Timestamp;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;

    import org.apache.james.security.DigestUtil;
    import org.apache.james.userrepository.DefaultUser;
    import org.apache.log4j.Logger;

    import com.ookang.entity.User;
    import com.ookang.jdbc.DBHelper;
    import com.ookang.jdbc.ServiceException;

    /**
     * 邮箱用户业务
     * 
     * @author 李赞红
     * 
     */

    public class EmailUserServiceImpl implements IEmailUseService, IService {
    private static String ALGORITHM = "SHA";
    private static Logger log = Logger.getLogger(DBHelper.class);

    /**
    * 判断指定用户名是否存在
    * @param userName
    * @return
    */
    public boolean isExist(String userName) {
    String sql = "select * from users where username=?";
    DBHelper dbHelper = DBHelper.getInstance();
    Object obj = dbHelper.getOne(sql, userName);
    log.debug(obj);
    return obj != null;
    }

    /**
    * 注册用户
    * @param userName
    *            用户名
    * @param password
    *            明文密码
    * @return true: 注册成功, false: 注册失败
    */
    public boolean regUser(User user) {
    if (this.isExist(user.getUserName())){
    return false;
    }

    try {
    String hashGuess = DigestUtil.digestString(user.getPassword(),ALGORITHM);
    DBHelper dbHelper = DBHelper.getInstance();

    String sql1 = "insert into users(username, pwdHash, pwdAlgorithm) values(?, ?, 'SHA')";
    String sql2 = "insert into user_extends(username,regTime,birthday,sex,nick) values(?,?,?,?,?)";

    dbHelper.update(sql1, user.getUserName(), hashGuess);

    dbHelper.update(sql2, user.getUserName(), new Date(), user
    .getBirthday(), user.getSex(), user.getNick());
    return true;

    } catch (NoSuchAlgorithmException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    throw new ServiceException(e);
    }
    }
    /**
    * 登陆
    * @param userName 用户名
    * @param password 明文密码
    * @return 返回用户的详细信息
    */
    public User login(String userName, String password){
    String sql = "select u.username,u.pwdHash, e.regTime, e.birthday, e.sex, e.nick from users u inner join user_extends e on u.username=e.username where u.username=?";
    List<Map<String, Object>> list = DBHelper.getInstance().query(sql, userName);
    if(list != null && list.size() > 0){
    User userEntity = this.wrap(list.get(0));
    org.apache.james.services.User user = new DefaultUser(userName, userEntity.getPassword(), "SHA");
    boolean v = user.verifyPassword(password);
    if(v){
    return userEntity;
    }else{
    return null;
    }
    }
    return null;
    }
    /**
    * 修改密码
    * @param userName 用户名
    * @param oldPassword 旧密码(明文)
    * @param newPassword 新密码(明文)
    * @return
    */
    public PasswordStatus modifyPassowrd(String userName, String oldPassword, String newPassword){
    User user = this.login(userName, oldPassword);
    if(user == null){
    return PasswordStatus.OLD_ERROR;
    }else{
    String sql = "update users set pwdHash=? where username=?";
    try {
    String hashGuess = DigestUtil.digestString(newPassword,
    ALGORITHM);
    try {
    int v = DBHelper.getInstance().update(sql, hashGuess, userName);
    if(v > 0){
    return PasswordStatus.SUCCESS;
    }else{
    return PasswordStatus.FAILURE;
    }
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    return PasswordStatus.FAILURE;
    }
    } catch (NoSuchAlgorithmException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    return PasswordStatus.FAILURE;
    }
    /**
    * 查出所有用户
    * @return
    */
    public List<User> queryAllUser(){
    String sql = "select * from users u left outer join user_extends e on u.username=e.username order by e.regTime";
    List<Map<String, Object>> list = DBHelper.getInstance().query(sql);
    List<User> listUser = new ArrayList<User>();
    for(Map<String, Object> map : list){
    listUser.add(this.wrap(map));
    }
    return listUser;
    }
    private User wrap(Map<String, Object> map){
    if(map == null) return null;
    User user = new User(
    (String)map.get("username"),
    (String)map.get("pwdHash"),
    (Timestamp)map.get("regTime"),
    (java.sql.Date)map.get("birthday"),
    (String)map.get("sex"),
    (String)map.get("nick")
    );
    log.debug(user);
    return user;
    }
    }

    第八步:业务类工厂
    package com.ookang.service;

    import com.aptech.proxy.ProxyFactory;

    public class ServiceFactory {
    public static String EMAILUSERSERVICE = "com.ookang.service.EmailUserServiceImpl";
    public static IService getInstance(String className){
    try {
    Object obj = Class.forName(className).newInstance();
    return (IService) ProxyFactory.getProxyInstance(obj);
    } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    return null;
    }
    }

    第九步:测试类
    package com.ookang.test;

    import java.util.List;

    import junit.framework.Assert;

    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;

    import com.ookang.entity.User;
    import com.ookang.service.IEmailUseService;
    import com.ookang.service.PasswordStatus;
    import com.ookang.service.ServiceFactory;

    public class EmailUserServiceImplTest {
    private IEmailUseService service;

    @Before
    public void setUp() throws Exception {
    service = (IEmailUseService) ServiceFactory.getInstance(ServiceFactory.EMAILUSERSERVICE);
    }

    @After
    public void tearDown() throws Exception {
    service = null;
    }

    @Test
    public void testRegUser() {
    User user = new User("zane", "zane");
    boolean v = service.regUser(user);
    Assert.assertEquals(true, v);
    }

    @Test
    public void testLogin(){
    User user = service.login("zane", "zane");
    Assert.assertNotNull(user);
    }
    @Test
    public void testModifyPassword(){
    PasswordStatus ps = service.modifyPassowrd("love", "love", "aaaaaa");
    System.out.println(ps);
    }
    @Test
    public void testQueryAllUser(){
    List<User> list = service.queryAllUser();
    Assert.assertEquals(7, list.size());
    }
    }
上一篇:有理想的程序员必须知道的15件事
下一篇:使用JavaMail发邮件
分享到:

版权所有 ©株洲健坤科技职业培训学校    学校地址:株洲市天元区黄山路205号健坤大厦(天元区消防中队对面)

咨询报名热线:400-8812-866    邮箱地址:4008812866@b.qq.com   备案号: 湘ICP备10202015号  

北大青鸟学费是多少 湖南北大青鸟怎么样
株洲北大青鸟好不好 株洲北大青鸟学费多少 株洲北大青鸟学校这么样
秒速时时彩官方网站