MVC&事务补充&反射补充

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,知道就行了

猜你喜欢

转载自blog.csdn.net/JQ210245253/article/details/89390495