JSP模式&JDBC加强

来源:互联网 发布:91助手苹果mac版下载 编辑:程序博客网 时间:2024/05/22 17:25

JSP模式&JDBC高级

今日任务

Ø 使用MVC设计模式开发一个转账案例

教学导航

教学目标

掌握JSP的设计模式

了解MYSQL的事务管理

掌握JDBC的事务管理

会使用DBUtils完成事务的管理

教学方法

案例驱动法

1.1 案例一:使用MVC设计模式完成转账的案例:

1.1.1 需求:

在页面中输入几个信息:收款人,付款人,转账金额.点击提交按钮可以完成转账的功能.在转账的过程中进行事务的管理.

1.1.2 分析:

1.1.2.1 技术分析:

JSP的模式】

 

Model1:JSP+JavaBean  比较适合开发一些小应用.

Model2:JSP+Servlet+JavaBean 可以开发任何一个应用. MVC设计模式:

MVC设计模式】

 

MVC是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

【EE开发中的三层结构

JavaEE开发中通常将一个应用分成:WEB层,业务层,持久层

 

JavaBean

JavaBeanJavaBean就是一个满足的特定格式要求的Java.

* 有一个无参数的构造:

* 属性私有

* 私有属性提供publicgetset方法.

 

属于Model1年代的标签.数据提交到JSP.

<jsp:useBean>

<jsp:setProperty>

<jsp:getProperty>

 

<jsp:useBean id="u" class="com.itheima.domain.User" scope="page"></jsp:useBean>

<jsp:setProperty property="*" name="u"/>

 

<jsp:getProperty property="username" name="u"/>

<jsp:getProperty property="password" name="u"/>

 

【事务管理-JDBC

Ø 事务的概述:

什么是事务:事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.

事务作用:保证在一个事务中多次操作要么全都成功,要么全都失败.

Ø MYSQL中的事务管理:

创建一个表:账户表.

create database web13;

use web13;

create table account(

id int primary key auto_increment,

name varchar(20),

money double

);

 

insert into account values (null,'守义',10000);

insert into account values (null,'凤儿',10000);

insert into account values (null,'如花',10000);

 

进行事务的管理:

MYSQL中可以有两种方式进行事务的管理:MYSQL数据库默认事务是自动提交的.写一条SQL语句,事务就已经提交了.Oracle数据库事务不自动提交.手动执行commit;

一种:手动开启事务的方式:

 * start transaction;

 * update account set money=money-1000 where name='守义';

 * update account set money=money+1000 where name='凤儿';

 * commit;或者rollback;

 

二种:设置MYSQL中的自动提交的参数:

show variables like '%commit%';

 

* 设置自动提交的参数为OFF:

set autocommit = 0;  -- 0:OFF  1:ON

JDBC进行事务管理】

Connection

* 事务管理的API

    

    

    

1.1.2.2 步骤分析:

Ø 步骤一:设计页面三个文本框.

Ø 步骤二:提交到Servlet

n 接收参数:

n 封装参数:

n 调用业务层处理数据

n 页面跳转

1.1.3 代码实现:

设计转账的页面:

<h1>转账的页面</h1>

<form action="" method="post">

<table border="1" width="400">

<tr>

<td>付款人:</td>

<td><input type="text" name="from"/></td>

</tr>

<tr>

<td>收款人:</td>

<td><input type="text" name="to"/></td>

</tr>

<tr>

<td>转账金额:</td>

<td><input type="text" name="money"/></td>

</tr>

<tr>

<td colspan="2"><input type="submit" value="转账"/></td>

</tr>

</table>

</form>

 

Servlet

/**

 * 转账的Servlet

 */

public class AccountServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

 

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

/**

 * 1.接收数据:

 * 2.封装数据:

 * 3.调用业务层:

 * 4.页面跳转作出响应:

 */

request.setCharacterEncoding("UTF-8");

// 接收数据:

String from = request.getParameter("from");

String to = request.getParameter("to");

double money = Double.parseDouble(request.getParameter("money"));

// 调用业务层:

AccountService accountService = new AccountService();

accountService.transfer(from,to,money);

}

 

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

 

 

}

 

Service

public class AccountService {

 

/**

 * 业务层转账的方法:

 * @param from:付款人

 * @param to:收款人

 * @param money:转账金额

 */

public void transfer(String from, String to, double money) {

// 调用DAO

AccountDao accountDao = new AccountDao();

try {

accountDao.outMoney(from, money);

// int d = 1/0;

accountDao.inMoney(to, money);

} catch (SQLException e) {

e.printStackTrace();

}

}

}

 

DAO

public class AccountDao {

 

/**

 * 付款的方法

 * @param name

 * @param money

 * @throws SQLException

 */

public void outMoney(String name,double money) throws SQLException{

Connection conn = null;

PreparedStatement pstmt = null;

try{

// 获得连接:

conn = JDBCUtils.getConnection();

// 编写一个SQL:

String sql = "update account set money = money-? where name=?";

// 预编译SQL:

pstmt = conn.prepareStatement(sql);

// 设置参数:

pstmt.setDouble(1, money);

pstmt.setString(2, name);

// 执行SQL

pstmt.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

pstmt.close();

conn.close();

}

}

/**

 * 收款的方法

 * @param name

 * @param money

 * @throws SQLException

 */

public void inMoney(String name,double money) throws SQLException{

Connection conn = null;

PreparedStatement pstmt = null;

try{

// 获得连接:

conn = JDBCUtils.getConnection();

// 编写一个SQL:

String sql = "update account set money = money+? where name=?";

// 预编译SQL:

pstmt = conn.prepareStatement(sql);

// 设置参数:

pstmt.setDouble(1, money);

pstmt.setString(2, name);

// 执行SQL

pstmt.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

pstmt.close();

conn.close();

}

}

}

 

通过两种Service编写完成事务的管理:

* 一种向下传递Connection

* 二种将Connection绑定到当前的线程中:

1.1.4 总结:

1.1.4.1 事务的特性:

事务有四大特性:

* 原子性:强调事务的不可分割.

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

* 隔离性:一个事务在执行的过程中,不应该受到其他事务的干扰.

* 持久性:事务一旦结束,数据就持久到数据库中.

1.1.4.2 如果不考虑事务的隔离性,引发一些安全性问题:

     读问题:三类

    * 脏读:一个事务读到了另一个事务未提交的数据.

    * 不可重复读:一个事务读到了另一个事务已经提交(update)的数据.引发一个事务中的多次查询结果不一致.

    * 虚读/幻读:一个事务读到了另一个事务已经提交的(insert)数据.导致多次查询的结果不一致

1.1.4.3 解决读问题:

设置事务的隔离级别:

* read uncommitted:脏读,不可重复读,虚读都可能发生.

* read committed:避免脏读,但是不可重复读和虚读有可能发生.

* repeatable read:避免脏读和不可重复读,但是虚读有可能发生的.

* serializable:避免脏读,不可重复读和虚读.(串行化的-不可能出现事务并发访问)

 

安全性:serializable > repeatable read > read committed > read uncommitted

效率 :serializable< repeatable read < read committed < read uncommitted

MYSQL:repeatable read

Oracle:read committed

1.1.4.4 演示脏读的发生

步骤一:分别开启两个窗口A,B:

步骤二:分别查询两个窗口的隔离级别:

* select @@tx_isolation

步骤三:设置A窗口的隔离级别为read uncommitted

* set session transaction isolation level read uncommitted;

步骤四:在两个窗口中分别开启事务:

* start transaction;

步骤五:在B窗口中转账操作:

* update account set money = money - 1000 where name='守义';

* update account set money = money + 1000 where name='凤儿';

**** B窗口中没有提交事务的!!!

步骤六:在A窗口中进行查询:

* 已经转账成功!!!(脏读:一个事务中读到了另一个事务未提交的数据)

1.1.4.5 演示避免脏读,不可重复读发生

步骤一:分别开启两个窗口A,B:

步骤二:分别查询两个窗口的隔离级别:

* select @@tx_isolation

步骤三:设置A窗口的隔离级别为read committed;

* set session transaction isolation level read committed;

步骤四:在两个窗口中分别开启事务:

* start transaction;

步骤五:在B窗口中完成转账的操作:

* update account set money = money - 1000 where name='守义';

* update account set money = money + 1000 where name='凤儿';

**** B窗口中没有提交事务!!!

步骤六:在A窗口中进行查询:

* 没有转账的结果!!!(已经避免了脏读)

步骤七:在B窗口中提交事务!!!

* commit;

步骤八:在A窗口中进行查询:

* 转账成功!!!(不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致一次事务中多次查询结果不一致.

1.1.4.6 演示避免不可重复读:

步骤一:分别开启两个窗口A,B:

步骤二:分别查询两个窗口的隔离级别:

* select @@tx_isolation

步骤三:设置A窗口的隔离级别为repeatable read;

* set session transaction isolation level repeatable read;

步骤四:在两个窗口中分别开始事务:

* start transaction;

步骤五:在B窗口中完成转账的操作:

* update account set money = money - 1000 where name='守义';

* update account set money = money + 1000 where name='凤儿';

***** B窗口没有提交事务!!!

步骤六:在A窗口中进行查询:

* 没有转账成功!!!(已经避免脏读)

步骤七:在B窗口提交事务!!!

* commit;

步骤八:在A窗口进行查询:

* 转账没有成功!!(避免不可重复读)

步骤九:在A窗口中结束事务,再重新查询.

1.1.4.7 演示隔离级别为serializable

步骤一:分别开启两个窗口A,B:

步骤二:分别查询两个窗口的隔离级别:

* select @@tx_isolation

步骤三:设置A窗口的隔离级别为serializable;

* set session transaction isolation level serializable;

步骤四:在两个窗口中分别开启事务:

* start transaction;

步骤五:在B窗口中完成一个insert操作:

* insert into account values (null,'芙蓉',10000);

***** 事务没有提交!!

步骤六:在A窗口中进行查找:

* 没有任何结果!!!(不可以事务并发执行的)

步骤七:在B窗口中结束事务!

1.1.4.8 JDBC的隔离级别的设置:(了解)

Connection中的方法:

 

Connection中提供了隔离级别的常量:

 

1.1.4.9 使用DBUtils的进行事务的管理:

QueryRunner

    * 构造:

    QueryRunner();

    QueryRunner(DataSource ds);

 

    * 方法:

    T query(String sql,ResultSetHanlder<T> rsh,Object... params);

    T query(Connection conn,String sql,ResultSetHanlder<T> rsh,Object... params);

    int update(String sql,Object... params);

    int update(Connection conn,String sql,Object... params);

 

方法分类:

    * 没有事务:

    QueryRunner(DataSource ds);

    T query(String sql,ResultSetHanlder<T> rsh,Object... params);

    int update(String sql,Object... params);

 

    * 有事务:

    QueryRunner();

    T query(Connection conn,String sql,ResultSetHanlder<T> rsh,Object... params);

    int update(Connection conn,String sql,Object... params);