zoukankan      html  css  js  c++  java
  • 事务&数据库连接池&DBUtils&JSP设计模式

    1. 事务

    1. Transaction:一组操作中,包含有很多单一的逻辑。只要有一个逻辑没有执行成功,都算失败。所有的数据都回归到最初的状态。

    2. 为什么要有事务:为了确保逻辑的成功,例如:银行的转账

    3. 使用命令行方式

    可以使用set autocommit = off;来关闭自动提交

    update account set money = money - 100 where id = 1;

    此时虽然命令行中查询的结果会发生改变,但是可以使用

    commit:提交事务,数据将会写到磁盘上面的数据库

    rollback:数据回滚,回到最初的状态

    选择是否提交

    4. 使用代码的方式

    事务是针对连接的

    public void testTransaction(){
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            con = JDBCUtil.getCon();
            // 默认的连接事务是自动提交的,关闭自动提交
            con.setAutoCommit(false);
            String sql = "update account set money = money - ? where id = ?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, 100);
            ps.setInt(2, 1);
            ps.executeUpdate();
            
            ps.setInt(1, -100);
            ps.setInt(2, 1);
            ps.executeUpdate();
            
            con.commit();
            
            /*while(rs.next()){
                String name = rs.getString("name");
                int money = rs.getInt("money");
                System.out.println("name:" + name + ",money:" + money);
            }*/
        } catch(Exception e){
            try {
                con.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally{
            JDBCUtil.release(con, ps, rs);
        }
    }

    5. 事务的特性

    a. 原子性:事务中包含的逻辑不可分割

    b. 一致性:事务执行前后数据的完整性保持一致

    c. 持久性:事务执行成功,数据永久保存在磁盘上面

    d. 隔离性:事务在执行期间,不应该受到其它事务的影响

    6. 事务的安全隐患

    不考虑隔离级别的问题,可能会出现以下的问题

    a. 读:脏读,不可重复读,幻读

    (1). 脏读:一个事务读到了另一个事务还未提交的事务

    mysql查看隔离级别

    默认是可重复读

    隔离级别默认四种:Read Uncommitted:读未提交

    Read Committed:读已提交

    Repeatable Read:重复度

    Serializable:可串行化

    设置A窗口的隔离级别为读未提交

    脏读出现的情况:两个窗口都开启事务:start transaction,回滚前和回滚后的结果不一致

     (2). 不可重复度

    设置a窗口的隔离级别为读已提交

    a,b两个窗口都开启事务,在B窗口中执行更新操作

    在a窗口中两次查询的结果不一致,一次是在b提交事务之前,一次是在b提交事务之后

    这个隔离界别可以避免脏读的现象,但是引发了另一个问题,不可重复读

    按照效率划分,从高到低

    读未提交>读已提交>可重复读>可串行化

    按照拦截程度,从高到低

    可串行化>可重复度>读已提交>读未提交

    (3). 可重复读

    设置a窗口为可重复读

    a,b两个窗口都开启事务,在b窗口中执行更新操作,b提交了数据以后,a窗口的数据也不会发生变化

    (4). 幻读:一个事务读取到了另一个事务已提交的插入的数据,导致多次查询的结果不一致

    (5). 可串行化:如果有一个连接的隔离级别为串行化,那么谁先打开了事务,谁就有了先执行的权力,谁后打开事务,谁就只能等待,等待前面的那个事务提交或者回滚以后,才能执行。但是这种隔离级别很少用,容易造成性能上面的问题,性能比较低

    b. 写:丢失更新

    事务总结:

    1. 在代码中使用事务

    setAutoCommit(false)

    conn.commit();

    conn.rollback();

    2. 事务只是针对连接对象,如果再开一个连接对象,那么默认的提交

    3. 事务是会自动提交的

    Oracle:默认的是读已提交

    mysql:默认的是可重复读

    7. 丢失更新

    两个事务A,B,A先更新,B后更新,会导致A的数据被覆盖

    A先更新,B后回滚,导致数据还是以前的数据,

    8. 解决丢失更新

    悲观锁:可以在查询时加入for update

    乐观锁:要求程序员自己控制

    2. 数据库连接池:

    1. 数据库的连接对象创建工作,比较消耗性能

    2. 一开始在内存中开创一个空间,先向连接池中放置多个连接对象,后面需要连接的话,直接到池子中去,不要去自己创建连接了,使用完毕,需要归还连接,确保连接对象可以循环利用。

     开源连接池:

    DBCP:

    a. 导入commons-dbcp-1.4.jar和commons-pool-1.5.6.jar文件

    b. 配置dbcpconfig.properties文件

     

    c. 编写代码

    public void testDBCP(){
        // 构建数据源对象
        // 连接的数据库的类型,名称,用户名,密码
        
        // 得到连接对象
        Connection con = null;
        PreparedStatement ps = null;
        try {
            BasicDataSourceFactory factory = new BasicDataSourceFactory();
            Properties properties = new Properties();
            InputStream is = DBCPDemo.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            properties.load(is);
            DataSource dataSource = factory.createDataSource(properties);
            con = dataSource.getConnection();
            String sql = "insert into account values(null, ?, ?)";
            ps = con.prepareStatement(sql);
            ps.setString(1, "浴帽");
            ps.setInt(2, 3000);
            ps.executeUpdate();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            JDBCUtil.release(con, ps);
        }
        
    }

    C3P0:

    a. 导入c3p0-0.9.1.2.jar的包

    b. 编写代码

    @Test
    public void C3P0Test(){
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        Connection con = null;
        PreparedStatement ps = null;
        try{
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            con = dataSource.getConnection();
            String sql = "insert into account values(null, ?, ?)";
            ps = con.prepareStatement(sql);
            ps.setString(1, "xiaochogn");
            ps.setInt(2, 5000);
            ps.executeUpdate();
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            JDBCUtil.release(con, ps);
        }
    }

    c. 使用配置文件的方式

    导入配置文件:c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    
        <!-- default-config 默认的配置,  -->
      <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost/bank</property>
        <property name="user">root</property>
        <property name="password">root</property>
        
        
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
      </default-config>
      
       <!-- This app is massive! -->
      <named-config name="oracle"> 
        <property name="acquireIncrement">50</property>
        <property name="initialPoolSize">100</property>
        <property name="minPoolSize">50</property>
        <property name="maxPoolSize">1000</property>
    
        <!-- intergalactoApp adopts a different approach to configuring statement caching -->
        <property name="maxStatements">0</property> 
        <property name="maxStatementsPerConnection">5</property>
    
        <!-- he's important, but there's only one of him -->
        <user-overrides user="master-of-the-universe"> 
          <property name="acquireIncrement">1</property>
          <property name="initialPoolSize">1</property>
          <property name="minPoolSize">1</property>
          <property name="maxPoolSize">5</property>
          <property name="maxStatementsPerConnection">50</property>
        </user-overrides>
      </named-config>
    
     
    </c3p0-config>
        
    @Test
    public void C3P0Test(){
        ComboPooledDataSource dataSource = new ComboPooledDataSource();// 里面可以写"orcle",或者"mysql",默认找default-config
        Connection con = null;
        PreparedStatement ps = null;
        try{
            con = dataSource.getConnection();
            String sql = "insert into account values(null, ?, ?)";
            ps = con.prepareStatement(sql);
            ps.setString(1, "chegnyang");
            ps.setInt(2, 5000);
            ps.executeUpdate();
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            JDBCUtil.release(con, ps);
        }
    }

     3. DBUtils

    DBUtils是apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能

    查询方法有两种:实现接口,使用框架写好的实现类

    package com.fengying.dbutils;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.ResultSetHandler;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.junit.Test;
    
    import com.fengying.domain.Account;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class TestDBUtils {
        @Test
        public void testUtils(){
            // dbutils只是简化了crub的代码,但是连接的创建以及获取工作,不在考虑范围之内
            QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
            try {
    //            queryRunner.update("insert into account values(null, ?, ?)", "xiaocong", 10000);
    //            queryRunner.update("delete from account where id = ?", 5);
    //            queryRunner.update("update account set money = ? where id = ?", 100000, 3);
                // 执行查询,查询到的数据在result中,但是需要用户手动封装
                // 方法一:
                Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<Account>(){
                    public Account handle(ResultSet rs) throws SQLException{
                        Account account = new Account();
                        while(rs.next()){
                            String name = rs.getString("name");
                            int money = rs.getInt("money");
                            account.setName(name);
                            account.setMoney(money);
                        }
                        return account;
                    }
                }, 6);
                System.out.println(account.toString());
                
                // 方法二:
                Account acc = queryRunner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), 6);
                System.out.println(acc.toString());
                
                // 查询数据集
                List<Account> acclist = queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
                for(Account at : acclist){
                    System.out.println(at.toString());
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    package com.fengying.domain;
    
    public class Account {
        private String name;
        private int money;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getMoney() {
            return money;
        }
    
        @Override
        public String toString() {
            return "Account [name=" + name + ", money=" + money + "]";
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
    }
    package com.fengying.domain;
    
    public class Account {
        private String name;
        private int money;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getMoney() {
            return money;
        }
    
        @Override
        public String toString() {
            return "Account [name=" + name + ", money=" + money + "]";
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
    }

    Meata data 

    元数据:用于描述数据的数据,String sql,描述这份sql字符串的数据叫做元数据

    数据库元数据:DatabaseMetaData

    参数元数据:ParameterMetaData

    结果集元数据:ResultSetMetaData

    4. JSP开发模式

    1. 开发模式1:JavaBean  + JSP,在jsp中直接写java代码,维护起来比较困难,页面代码变得臃肿

    <%

      封装数据

      执行业务

      准备数据

    %>

    2. 开发模式2:Servlet + JavaBean + JSP,即是MVC模式

    M:Model,模型层,封装数据JavaBean

    V:View,视图层,jsp,只关注于显示

    C:Controller,控制层,Servlet接收页面的请求,找模型层去处理,然后响应

    大型项目:分层:模型比较清楚 ,便于维护,扩展方便

    小型项目:严格遵守MVC,可能比较麻烦,代码稍多

    3. 三层架构

    Web层:servlet/jsp

    业务逻辑层:Ejb,会话bean,javadBean

    数据访问层:Dao

     学生管理系统:

    a. 查询

      1. 写一个jsp页面,里面放置一个超链接

      2. 写servlet,接收请求,去调用service,由service去调用dao

      3. 先写Dao,做Dao实现

      4. 用Service,做Service实现

      5. 在servlet存储数据,并且做出页面响应

      6. 在list.jsp上显示数据
    b. 增加

      1. 先跳转到增加的页面,编写增加的页面

      2. 点击添加,提交数据到AddServlet, 处理数据

      3. 调用service

      4. 调用dao,完成数据持久化

      5. 完成这些存储工作,跳转到列表页面,这里不能跳转到列表页面,应该先跳转到查询所有学生信息的servlet,由那个Servlet再去调用列表页面

      6. 爱好value的值有多个,做相关的处理

    c. 删除

    1. 点击超链接,弹出一个询问是否删除的对话框,如果点击了确定,那么确定删除

    2. 让超链接执行一个js方法

    3. 在js访问里面加入判断点击的选项,然后跳转到servlet

    4. servlet中收到了请求,然后去调用service,service去调用dao

    d. 更新

    1. 点击列表上面的更新,先跳转到一个EditStudentServlet

      在这个Servlet里面,先根据ID,去查询这个学生的所有信息出来

    2. 跳转到更新的页面,在页面上面显示数据

      姓名,电话文本框,使用Jstl表达式

     

      个人简介文本域:<textarea name="info" rows="3" cols="20">${stu.info}</textarea>

      性别单选框需要用到jstl表达式:

      爱好多选框引入jstl的functions函数

    3. 修改完毕以后,提交数据到UpdateServlet

      提交的数据没有带id,需要创建一个隐藏的Input,给定id的值,然后提交表单

    4. 获取数据,调用service,调用dao

  • 相关阅读:
    七牛图片上传JSSDK
    2015年12月中国航空公司名录
    HTML5 开发框架
    利用HTML5定位功能,实现在百度地图上定位
    openerp7 时区问题
    AS3使用Json 传复杂数据 -------- 用数组而不是向量
    随便写写
    生产环境该如何选择lvs的工作模式,和哪一种算法
    获取Linux权限后安装rootkit
    IT求职经验分享
  • 原文地址:https://www.cnblogs.com/feng-ying/p/9852706.html
Copyright © 2011-2022 走看看