JDBC第二次学习

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

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

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

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

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

  3. 用JDBC实现接口。

  4. 用配置文件(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);
        }
    }

}
View Code

   用配置文件(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();  

   

   

posted @ 2016-03-26 17:37  叶十一少  阅读(264)  评论(0编辑  收藏  举报