1.MVC是什么:
M:model 模型 作用:主要是封装数据,封装对数据的访问
V:view 视图 作用:主要是用来展示数据 一般是jsp担任的
C:ctrl 控制 作用:接受请求,找到相应的javabean完成业务逻辑
2.Mvc思想
就是将业务逻辑,代码,显示相分离的一种思想
servlet-->缺点:生成html内容太麻烦(out.print("html"))
|
jsp--->缺点:阅读起来不方便,维护比较困难
|
jsp+javabean:
jsp的model1:
jsp:接受请求,展示数据
javabean:和数据打交道
|
jsp+javabean+servlet
jsp的model2:
jsp:展示数据
javabean:和数据打交道
servlet:接受请求,处理业务逻辑
就是MVC思想的体现
2.1
javabean在model1中的使用(了解)
javabean+jsp
用到的
1. Uer.java:包含username和password(创建的一个javabean)
2.form表单
注意:表单中用name和password
login.jsp:就是下面的东西
<!-- 接受值 -->
<jsp:useBean id="u" class="com.itheima.domain.User"></jsp:useBean><!--相当于 User u=new User()-->
<jsp:setProperty property="name" name="u"/><!--相当于 u.setName(...)-->
<jsp:setProperty property="password" name="u"/><!-- 打印值-->
<jsp:getProperty property="name" name="u"/>
useBean就可以用这样的方式将提交过来的数据封装成指定的对象
如何做的--》反射,获取class就可以干了
2.2
javabean在model2中的使用
BeanUtils:可以看作封装数据一个工具类
使用步骤:
1.导入jar包
2.使用BeanUtils.populate(Object bean,Map map);底层用的还是反射的思想(得到class,对象就可以干许多事情的)
传入map集合给bean对象,然后获取class,然后。获取其中的所有方法
map。获取key
获取value。
还是反射思想。
2.反射的回顾+补充
1.获取class对象
方式1:
Class clazz=Class.forName("全限定名")
方式2:
Class clazz=类名.class;
方式3:
Class clazz=对象.getClass;
2.可以获取对应类的构造方法(了解)
Constructor con = clazz.getConstructor(Class .. paramClass);
Person p = (Person) con.newInstance(参数);
3.可以通过clazz创建一个对象(了解)
clazz.newInstance();//相当于调用的无参构造器
如果没有无参构造的话就会报错
4.可以通过clazz获取所有的字段 getFiled()(了解中的了解)
5.可以通过clazz获取所有的方法
Method m = clazz.getMethod("sleep");//获取公共的方法
Method m = clazz.getDeclaredMethod("sleep");//获取任意的方法
注意:若是私有的方法 必须让该方法可以访问
m.setAccessible(true);
6.Method对象的invoke是有返回值,他的返回值就是目标方法执行的返回值
m.invoke(p);执行
注意:反射里面的参数传进去后得用.class
eg:Strring name->String.class
Demo.java60行
总结:
有了class对象之后,无所不能.
代码演示:
package com.itheima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.junit.Test;
import com.itheima.domain.Person;
public class Demo {
@Test
public void f1() throws Exception{
//1.获取class对象
Class clazz=Class.forName("com.itheima.domain.Person");
//2.获取构造器(了解)
//Constructor con = clazz.getConstructor();
//Person p = (Person) con.newInstance();
//2.1获取带参的构造器(了解)
Constructor con = clazz.getConstructor(String.class,String.class);
Person p = (Person) con.newInstance("tom","123");//相当于 new Person("tom","123")
System.out.println(p.getPassword());
}
@Test
public void f2() throws Exception{
//1.获取class对象
Class clazz=Class.forName("com.itheima.domain.Person");
//2.获取构造器(了解)
Person p = (Person) clazz.newInstance();
}
@Test
//获取私有方法
public void f3() throws Exception{
//1.获取class对象
Class clazz=Class.forName("com.itheima.domain.Person");
Person p = (Person) clazz.newInstance();
//2.获取方法
//Method m = clazz.getMethod("sleep");//获取公共的方法
Method m = clazz.getDeclaredMethod("sleep");//获取任意的方法
//2.1若是私有的方法 必须让该方法可以访问
m.setAccessible(true);
//执行方法
m.invoke(p);
}
@Test
public void f11() throws Exception{
//1.获取class对象
Class clazz=Class.forName("com.itheima.domain.Person");
//2.获取私有的方法
Method m = clazz.getDeclaredMethod("sleep", String.class);
//让方法可以访问
m.setAccessible(true);
//3.让方法执行
String res=(String) m.invoke(clazz.newInstance(),"侯振");
System.out.println(res);
}
}
3.java中如何体现mvc的思想,---->分层
分层:javaee的三层架构
web
作用:
展示数据 ----jsp
-----servlet-------
接受请求
找到对应的service,调用方法 完成逻辑操作
信息生成或者页面跳转
service 业务层
作用:
完成业务操作
调用dao
dao(data access object 数据访问对象)
作用:
对数据库的curd操作
4.案例实现-转账(体现三层模型&事务的重要性)
Test1:使用最原始的jdbc,不加事务。
步骤分析:
1.数据库和表
2.新建一个项目 day08
3.导入jar包和工具类
驱动 jdbcUtils
c3p0及其配置文件和工具类
dbutils
4.新建一个account.jsp 表单
5.accountservlet:
接受三个参数
调用accountservice.account方法完成转账操作
打印信息
6.account方法中:
使用jdbc不考虑事务
调用dao完成转出操作
调用dao完成转入操作
7.dao中
项目结构:
1.
2.
3.
位于根目录下的account.jsp
4.大致结果:
5.account.jsp-------->accountservlet
1. //0.设置编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter w = response.getWriter();//1.接受三个参数(来自.jsp页面的参数)
String fromUser=request.getParameter("fromuser");
String toUser=request.getParameter("touser");
String money=request.getParameter("money");//2.调用accountservice.account(fromuser,touser,money)
try {
new accountservice().account(fromUser, toUser, money);
} catch (Exception e) {
e.printStackTrace();
w.print("转账失败");
return ;
}
4. w.print("转账成功");
6.accountservlet---->accountservice
public void account_(String fromUser, String toUser, String money) throws Exception {
AccountDao dao = new AccountDao();
dao.accountOut(fromUser,money);
dao.accountIn(toUser,money);}
7.accountservice----->accountDao
public void accountIn(String toUser, String money) throws Exception {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn=JdbcUtils.getConnection();
//编写sql
String sql="update account1 set money = money + ? where name =?";
//创建语句执行者
st=conn.prepareStatement(sql);
//设置参数
st.setString(1, money);
st.setString(2, toUser);
//执行sql
int i = st.executeUpdate();
//处理
System.out.println("入:"+i);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
throw e;
}finally
{
JdbcUtils.closeResource(conn, st, rs);
}
}
分析:
表面虽然可以正常转账,但是:
一旦出现异常,钱飞了.
要想避免这事情,必须添加事务,在service添加事务(保证相同的结果).就必须获取一个 connection,con.setAutoCommit(false)
dao层也得获取链接,为保证统一性
为了保证所有的操作在一个事务中,必须保证使用的是同一个连接
在service层我们获取了连接,开启了事务.如何让dao层使用此连接呢????
Test2:向下传递参数
注意连接应该在service释放,不在dao层释放
=====》在service中开启事务
Connection conn=JdbuUtils.getconnection();
con.setAutoCommit(false)就开启了
但在什么时候提交,什么时候回滚呢
所有操作都 成功提交,出现异常回滚
在service中try的数据处理异常(1/0),但servlet不知道,所以要把异常throw出去
service:
public void account(String fromUser, String toUser, String money) throws Exception {
AccountDao dao = new AccountDao();
Connection conn=null;
try {
//0.开启事务
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
//1.转出
dao.accountOut(conn,fromUser,money);
//int i=1/0;
//2.转入
dao.accountIn(conn,toUser,money);
//3.事务提交
conn.commit();
JdbcUtils.closeConn(conn);
} catch (Exception e) {
e.printStackTrace();
//事务回滚
conn.rollback();
JdbcUtils.closeConn(conn);
throw e;
}
}
Dao:
public void accountOut(Connection conn,String fromUser, String money) throws Exception {
PreparedStatement st=null;
ResultSet rs=null;
try {
//编写sql
String sql="update account1 set money = money - ? where name =?";
//创建语句执行者
st=conn.prepareStatement(sql);
//设置参数
st.setString(1, money);
st.setString(2, fromUser);
//执行sql
int i = st.executeUpdate();
//处理
System.out.println("出:"+i);
} catch ( SQLException e) {
e.printStackTrace();
throw e;
}finally
{
JdbcUtils.closeStatement(st);
}
}
方法2:Threadlocal(绑定线程)
1.connection是本来就是DAO层的东西☆★★
还得被传过来
假如不想传递connection而且想要要同一个事务假如不想传递connection而且想要要同一个事务
从servlet那知道一次请求创建一个线程
可以将connection对象绑定当前线程上
jdk中有一个ThreadLocal类,
ThreadLocal 实例通常是类中的 private static 字段,
它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
ThreadLocal的方法:
构造:
new ThreadLocal()
set(Object(是个泛型) value):将内容和当前线程绑定
Object get():获取和当前线程绑定的内容
remove():将当前线程和内容解绑
ser,get,remove的形式,
===》实质
内部维护了map集合
map.put(当前线程,内容);
map.get(当前线程)
map.remove(当前线程)
值得注意的是:
我们可以在这里将线程绑定和开启事务封装到DataSource.Utils(原来是用来连接数据库的,dbutils是用来执行sql的不要搞混)
他们都用到了connection
封装后的DataSourceUtils.java
package com.javaweb.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class DataSourceUtils {
private static ComboPooledDataSource ds=new ComboPooledDataSource();
private static ThreadLocal<Connection> tl=new ThreadLocal<>();
/**
* 获取数据源
* @return 连接池
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接
* @return 连接
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
//不在直接获取连接return ds.getConnection();
//从当前线程获取
Connection conn = tl.get();
//但一个次获取的时候没有绑定,所以先判断一下
if(conn==null){
//第一次获取 创建一个连接 和当前的线程绑定
conn=ds.getConnection();
//绑定
tl.set(conn);
}
return conn;
}
/**
* 释放资源
*
* @param conn
* 连接
* @param st
* 语句执行者
* @param rs
* 结果集
*/
public static void closeResource(Connection conn, Statement st, ResultSet rs) {
closeResource(st, rs);
closeConn(conn);
}
public static void closeResource( Statement st, ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
}
/**
* 释放连接
*
* @param conn
* 连接
*/
public static void closeConn(Connection conn) {
if (conn != null) {
try {
conn.close();
//解绑
tl.remove();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
/**
* 释放语句执行者
*
* @param st
* 语句执行者
*/
public static void closeStatement(Statement st) {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st = null;
}
}
/**
* 释放结果集
*
* @param rs
* 结果集
*/
public static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
}
/**
* 开启事务
* @throws SQLException
*/
public static void startTransaction() throws SQLException{
//获取连接同时开启事务
getConnection().setAutoCommit(false);//手动的开启事务
}
/**
* 事务提交
*/
public static void commitAndClose(){
try {
//获取连接
Connection conn = getConnection();
//提交事务
conn.commit();
//释放资源
conn.close();
//解除绑定
tl.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 事务回滚
*/
public static void rollbackAndClose(){
try {
//获取连接
Connection conn = getConnection();
//事务回滚
conn.rollback();
//释放资源
conn.close();
//解除绑定
tl.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
servlet中:
new AccountService4tl().account(fromUser, toUser, money);servlet->service:
service:
package com.javaweb.service;import com.javaweb.dao.AccountDao4tl;
import com.javaweb.utils.DataSourceUtils;public class AccountService4tl {
/**
* 转账
* @param fromUser 转出方
* @param toUser 转入方
* @param money 金额
* @throws Exception
*/
public void account(String fromUser, String toUser, String money) throws Exception {
AccountDao4tl dao = new AccountDao4tl();
try {
//0.开启事务
DataSourceUtils.startTransaction();
//1.转出
dao.accountOut(fromUser,money);
//int i=1/0;
//2.转入
dao.accountIn(toUser,money);
//3.事务提交
DataSourceUtils.commitAndClose();
} catch (Exception e) {
e.printStackTrace();
DataSourceUtils.rollbackAndClose();
throw e;
}
}}
.
dao
package com.javaweb.dao;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;import com.javaweb.utils.DataSourceUtils;
public class AccountDao4tl {
public void accountOut(String fromUser, String money) throws Exception {
PreparedStatement st=null;
ResultSet rs=null;
Connection conn=null;
try {
conn=DataSourceUtils.getConnection();
//编写sql
String sql="update account1 set money = money - ? where name =?";
//创建语句执行者
st=conn.prepareStatement(sql);
//设置参数
st.setString(1, money);
st.setString(2, fromUser);
//执行sql
int i = st.executeUpdate();
//处理
System.out.println("出:"+i);
} catch ( SQLException e) {
e.printStackTrace();
throw e;
}finally
{
//JdbcUtils.closeStatement(st);
DataSourceUtils.closeResource(st, rs);
}
}
/**
*
* @param toUser
* @param money 基本同上
* @throws Exception
*/public void accountIn(String toUser, String money) throws Exception {
PreparedStatement st=null;
ResultSet rs=null;
Connection conn=null;
try {
//编写sql
conn=DataSourceUtils.getConnection();
String sql="update account1 set money = money + ? where name =?";
//创建语句执行者
st=conn.prepareStatement(sql);
//设置参数
st.setString(1, money);
st.setString(2, toUser);
//执行sql
int i = st.executeUpdate();
//处理
System.out.println("入:"+i);
} catch ( SQLException e) {
e.printStackTrace();
throw e;
}finally
{
/*JdbcUtils.closeStatement(st);*/
DataSourceUtils.closeResource(st, rs);
}
}
}
3.开发中不用jdbc 用他的框架 DButils:这里如何控制事务呢?
先来回顾一下Dbutils(这是在dao层中做的事情,导入jar包就直接用了,没前面那么复杂)
DButils:
1.创建queryrunner
2.编写sql
3.执行sql
他的API:QueryRunner:
构造:
new QueryRunner(DataSource ds):自动事务(这里的Datasourc已经把前面的部分都做好了)
new QueryRunner():手动事务
常用方法:
查询的时候不加事务一般是没问题的
是不加手动的事务
update(Connection conn,String sql,Object ... params):执行的cud操作
query(Connection conn....):执行查询操作
注意:
一旦使用手动事务,调用方法的时候都需要手动传入connection,并且需要手动关闭连接
servlet和service和前面的一样的,前面的就是封装成了DataSourceUtils这个工具类,改动名称即可
只用变得是DAO层中的
package com.javaweb.dao;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import com.javaweb.utils.DataSourceUtils;
public class AccountDao4DB {
public void accountOut(String fromUser, String money) throws SQLException {
// 创建queryrunner
QueryRunner qr = new QueryRunner();
//编写sql
String sql="update account1 set money =money - ? where name =?";
//执行sql
qr.update(DataSourceUtils.getConnection(), sql, money,fromUser);
}public void accountIn(String toUser, String money) throws SQLException {
QueryRunner qr=new QueryRunner();
String sql="update account1 set money =money + ? where name =?";
qr.update(DataSourceUtils.getConnection(), sql, money,toUser);
}}
是不是感觉简单了许多
1.
2.
3.成功之后
项目总结:
1.UPDATE account1 SET money=100
可以使aa和bb的钱都变成100
2.service中JdbcUtils.closeConn(conn)
传递在accountIn(Connection,fromUser,money)
accountOut(.....)中已经将connection作为参数传到dao中了,但你在他两执行之后是要关闭的。
3.jdbc.utils没有用上
到后面的时候好像没有用上,前面简单的用上了,不用管
4.两次问题的解决
1.一次是jdbc,utils中的day02没有改
此次项目用的是day08
2.aa和bb中的money的钱越界了,不够用改大了后就正常了
5.queryrunner()
new QueryRunner(DataSource ds):自动事务
他需要什么东西直接从DataSpurce中直接拿就可以了,不要要释放资源,他都已经做了
new QueryRunner();手动的话,连接数据库的话,必须要有连接,
用他的常用方法
update(connection,sql)以前不用connection,现在需要,他是从线程中获取的。
用无参的构造方法,他里面已经有事务了
update里已经就帮我们做了关闭了所有资源,里面的那个参数已经设置为false,知道就行了