MVC设计模式

MVC模式

MVC:Model、View、Controller

MVC设计模式的引入

在我们实际开发的最后到产品上线,供给客户使用,客户通过浏览器或者app等进行数据的操作,实现这个的有,处理发送请求,业务逻辑处理以及访问数据库,这三个功能我们是可以放到一块使用的,但是如果放在一起,代码便会很臃肿,不利于维护,于是便出现了代码分层思想,把代码按照功能分为三层,即模型层(Model)、显示层(View)、控制层(Controller),这种代码的组织架构就叫MVC模式

MVC分层图
在这里插入图片描述
模型层(Model):在模型层中又可以分为两层,即Service层和Dao层,这两层的主要功能是:

Service层:负责一些业务处理,比如说:获取数据库连接,关闭数据库连接,事务回滚或者一些复杂的逻辑业务处理
Dao层:(Database Accept Object)  负责访问数据库,对数据的操作,获取结果集,将结果集中的数据装到OV(Object Value)对象中,之后再返回给Service层

Controller层:主要功能是处理用户的请求
View层:主要负责显示数据(Html、Css、jQuery等等)

实现分层的组织包结构图
在这里插入图片描述
代码的调用顺序:
View>Controller>Service>Dao,如果上层代码对下层代码的依赖程度过高,就需要对每层的代码定义一个(标准)接口。
定义接口之后的包结构
在这里插入图片描述
Model层的Dao层设计思想:
为数据库中的emp表设计数据操作的Dao表,在实际开发过程中,Dao层需要先定义出自己的标准(接口),降低耦合度
在这里插入图片描述
1、新建一个项目
2、导入相关的开发包,比如驱动包(oracle,mysql,sql server等的驱动包)
3、构造出包的结构
在这里插入图片描述
4、创建emp对象,放在vo包下面,emp表中的字段名对应设置成javabean中的成员变量
在这里插入图片描述

package com.zhouym.mvcpro.vo;

import java.io.Serializable;
import java.util.Date;

@SuppressWarnings("serial")
public class Emp implements Serializable{
//对应EMP表中的字段名
	private Integer empno;
	private String ename;
	private String job;
	private Integer mgr;
	private Date hiredate;
	private Double sal;
	private Double comm;
	private Integer deptno;
	public Emp() {
		super();
	}
	public Emp(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double comm,
			Integer deptno) {
		super();
		this.empno = empno;
		this.ename = ename;
		this.job = job;
		this.mgr = mgr;
		this.hiredate = hiredate;
		this.sal = sal;
		this.comm = comm;
		this.deptno = deptno;
	}
	public Integer getEmpno() {
		return empno;
	}
	public void setEmpno(Integer empno) {
		this.empno = empno;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public Integer getMgr() {
		return mgr;
	}
	public void setMgr(Integer mgr) {
		this.mgr = mgr;
	}
	public Date getHiredate() {
		return hiredate;
	}
	public void setHiredate(Date hiredate) {
		this.hiredate = hiredate;
	}
	public Double getSal() {
		return sal;
	}
	public void setSal(Double sal) {
		this.sal = sal;
	}
	public Double getComm() {
		return comm;
	}
	public void setComm(Double comm) {
		this.comm = comm;
	}
	public Integer getDeptno() {
		return deptno;
	}
	public void setDeptno(Integer deptno) {
		this.deptno = deptno;
	}
	@Override
	public String toString() {
		return "Emp [empno=" + empno + ", ename=" + ename + ", job=" + job + ", mgr=" + mgr + ", hiredate=" + hiredate
				+ ", sal=" + sal + ", comm=" + comm + ", deptno=" + deptno + "]";
	}
	
}

注意的是:
1、简单的java类中,最好使用基本类型对应的包装类
2、实现Serializable接口,方便以后程序的拓展(比如说序列化该类对象就需要实现该接口)

5、开发Dao层的接口
定义一个父接口,定义一个适配器类,再定义子接口,让适配器类实现父接口,让一个子接口或者多个子接口继承父接口,再让一个真正的实现类去继承适配器类并且实现这些子接口,这样的好处是:可以让这个真正的实现类选择性的重写方法,根据实际需要选择要重写的方法,而不是被迫的全部重写这些方法。
父接口

package com.zhouym.mvcpro.dao;

import java.util.List;
import java.util.Set;

/**
 * @author zhouym
 *
 * @param <K> 在对应实体类中表示数据库主键值的字段类型
 * @param <V> 表示对应的实体类的类型
 */
public interface IBaseDAO<K,V> {
	/**
	 * 增加雇员数据的方法
	 * @param emp 保存要插入数据的对象
	 * @return
	 * @throws Exception
	 */
	public int insert(V v) throws Exception;
	
	/**
	 * 修改数据
	 * @param emp 保存要插入数据的对象
	 * @return
	 * @throws Exception 
	 */
	public  int update(V v) throws Exception;
	
	/**
	 * 根据编号删除数据
	 * @param K
	 * @return
	 * @throws Exception
	 */
	public int deleteById(K k) throws Exception;
	
	/**
	 * 批量删除数据
	 * @param k
	 * @return
	 * @throws Exception
	 */
	public int deleteBatch(Set<K> ids) throws Exception;
	
	
	/**
	 * @param K 根据编号查询数据
	 * @return
	 * @throws Exception
	 */
	public V selectById(K K) throws Exception;
	
	/**
	 * 模糊查询
	 * @param kw 查询的内容
	 * @param cp  表示当前页显示的数据量
	 * @param ls  表示当前页
	 * @return
	 * @throws Exception
	 */
	public List<V> selectSplitAll(String kw,Integer cp,Integer ls) throws Exception;
	
		
	/**
	 * 统计数据量
	 * @param kw 表示根据指定的参数显示统计量
	 * @return
	 * @throws Exception
	 */
	public int selectCount(String kw) throws Exception;
	
}

让适配器类实现父接口,子接口继承父接口
适配器类

package com.zhouym.mvcpro.adapter;

import java.util.List;
import java.util.Set;

import com.zhouym.mvcpro.dao.IBaseDAO;
//适配器类实现父接口
public class DAOAdapter<K,V> implements IBaseDAO<K, V>{

	@Override
	public int insert(V v) throws Exception {
		return 0;
	}

	@Override
	public int update(V v) throws Exception {
		return 0;
	}

	@Override
	public int deleteById(K k) throws Exception {
		return 0;
	}

	@Override
	public int deleteBatch(Set<K> ids) throws Exception {
		return 0;
	}

	@Override
	public V selectById(K K) throws Exception {
		return null;
	}

	@Override
	public List<V> selectSplitAll(String kw, Integer cp, Integer ls) throws Exception {
		return null;
	}

	@Override
	public int selectCount(String kw) throws Exception {		
		return 0;
	}

}

子接口
在子接口中,可以定义本接口特有的方法,也可以不写,默认继承了父类的所有方法

package com.zhouym.mvcpro.dao;

import com.zhouym.mvcpro.vo.Emp;

/**
 * 继承了父接口的所有方法(继承的是公共的方法)
 * @author zhouym
 *可以在本接口中定义特有的方法
 */
public interface IEmpDAO extends IBaseDAO<Integer,Emp>{
	
}

真正的实现类

package com.zhouym.mvcpro.dao.impl;

import java.sql.Connection;
import java.util.List;
import java.util.Set;
import com.zhouym.mvcpro.vo.DBUtil;
import com.zhouym.mvcpro.adapter.DAOAdapter;
import com.zhouym.mvcpro.dao.IEmpDAO;
import com.zhouym.mvcpro.vo.Emp;


/**
 * 通过适配器模式,继承适配器类,实现接口,可便于在本类中选择性的继承需要用到的方法
 * @author 
 *
 */
public class EmpDAOImpl extends DAOAdapter<Integer, Emp> implements IEmpDAO {
	//将数据库连接对象传进来
	private Connection conn;
	//定义无参构造以及带参构造
	public EmpDAOImpl() {
		super();
	}
	
	public EmpDAOImpl(Connection conn) {
		super();
		this.conn = conn;
	}
	
	@Override
	public int insert(Emp vo) throws Exception {
		String sql = "insert into emp(ename,job,sal,hiredate,mgr,comm,deptno) values(?,?,?,?,?,?,?)";
		System.out.println(sql);
		return DBUtil.save(conn, sql, vo, false);
	}

	@Override
	public int update(Emp vo) throws Exception {
		String sql = "update emp set mgr=? , hiredate=? where ename=?";
		System.out.println(sql);
		return DBUtil.edit(conn, sql, vo);
	}

	@Override
	public int deleteById(Integer empno) throws Exception {
		String sql="delete from emp where empno=?";
		return DBUtil.remove(conn, sql, empno);
	}
	//不需要的方法可以不用重写
	@Override
	public int deleteBatch(Set<Integer> ids) throws Exception {
		StringBuilder sql = new StringBuilder("delete from emp where empno in (");
		return DBUtil.remove(conn, sql, ids);
	}

	@Override
	public Emp selectById(Integer id) throws Exception {
		String sql = "select empno,ename,job,sal,hiredate,mgr,comm,deptno from emp where empno=?";
		return DBUtil.selectOne(conn, sql, Emp.class, id);
	}

	@Override
	public List<Emp> selectSplitAll(String kw, Integer cp, Integer ls) throws Exception {
		
		return DBUtil.selectSplitAll("A", 1, 3);
	}

	@Override
	public int selectCount(String kw) throws Exception {
		String sql = "select count(*) from emp where ename like ?";
		return DBUtil.selectCount(conn, sql, "%"+kw+"%");
	}

}

最后在这个实现类中的方法内调用工具类中的方法

package com.zhouym.mvcpro.vo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class DBUtil {
	private static final int[] RETURN_GENERATED_KEYS = null;
	private static PreparedStatement pst;
	private static ResultSet rst;

	/**
	 * 实现数据的增加
	 * 
	 * @param conn
	 *            连接对象名
	 * @param sql
	 *            sql语句对象
	 * @param vo
	 *            Volue Object
	 * @param getAutoKey
	 *            是否需要主动增长的主键值,如果getAutoKey为True,则需要返回自动增长的主键值
	 * @return 返回收到影响的行数
	 * @throws Exception
	 */
	public static <T> int save(Connection conn, String sql, T vo, boolean getAutoKey) throws Exception {
		pst = conn.prepareStatement(sql,RETURN_GENERATED_KEYS);

		// 为sql中的占位符“?”赋值
		// sql = "insert into emp(ename,job,sal,hiredate,mgr,comm,deptno)
		// values(?,?,?,?,?,?,?)"
		// 分割后:ename,job,sal,hiredate,mgr,comm,deptno
		String[] columns = sql.split("\\(")[1].split("\\)")[0].split(",");
		// 取得vo类对应的class类对象 vo=emp
		Class<?> cls = vo.getClass();
		// 遍历字符串数组
		for (int i = 0; i < columns.length; i++) {
			// 获取当前字段对应在vo类中属性的值,调用对应的getter方法获取
			Method m = cls.getDeclaredMethod("get" + upperCase(columns[i]));
			// 调用方法取得值
			Object fvalue = m.invoke(vo);

			// 将这个值作为占位符的内容
			pst.setObject(i + 1, fvalue);
		}
		// 对关键字段的自增长的判断
		if (getAutoKey) {
			rst = pst.getGeneratedKeys();
			if (rst.next()) {
				return rst.getInt(1);
			}

		} else {
			return pst.executeUpdate();
		}
		return 0;
	}

	public static <T> int edit(Connection conn, String sql, T vo) throws Exception {
		PreparedStatement pst = conn.prepareStatement(sql);
		String[] columns = sql.split("set")[1].split("where")[0].split(",");
		// 获取类对象
		Class<?> cls = vo.getClass();

		// 遍历数组
		for (int i = 0; i < columns.length; i++) {
			// 获取字段名
			String fieldName = columns[i].split("=")[0].trim();

			// 获取cls类中的字段方法
			Method m = cls.getDeclaredMethod("get" + upperCase(fieldName));

			// 获取字段值
			Object fvalue = m.invoke(vo);

			pst.setObject(i + 1, fvalue);

		}

		// 分割判断条件的字符,获取判断条件字段名
		String conditionFieldName = sql.split("where")[1].split("=")[0].trim();

		Object ob = cls.getDeclaredMethod("get" + upperCase(conditionFieldName)).invoke(vo);
		pst.setObject(columns.length + 1, ob);

		return pst.executeUpdate();
	}

	public static int remove(Connection conn, String sql, Object... params) throws SQLException {
		pst = conn.prepareStatement(sql);
		for (int i = 0; i < params.length; i++) {
			pst.setObject(i + 1, params[i]);
		}
		return pst.executeUpdate();
	}

	public static int remove(Connection conn, StringBuilder sql, Set<Integer> ids) throws Exception {

		Iterator<Integer> iter = ids.iterator();
		// 遍历迭代器
		while (iter.hasNext()) {
			sql.append(iter.next() + ",");
		}
		sql.delete(sql.length() - 1, sql.length());

		sql.append(")");
		PreparedStatement pst = conn.prepareStatement(sql.toString());

		return pst.executeUpdate();
	}

	public static <T> T selectOne(Connection conn, String sql, Class<T> clz, Object... params) throws Exception {
		pst = conn.prepareStatement(sql);
		for (int i = 0; i < params.length; i++) {
			pst.setObject(i + 1, params[i]);
		}
		ResultSet rst = pst.executeQuery();
		T t = null;
		if (rst.next()) {
			t = clz.newInstance();
			// 获取所有属性
			Field[] fs = clz.getDeclaredFields();
			for (int i = 0; i < fs.length; i++) {
				// 获取属性名
				String fname = fs[i].getName();
				// 获取属性对应的方法名
				Method m = clz.getDeclaredMethod("get" + upperCase(fname));

				// 根据属性名从结果集中获取数据
				Object fvalue = null;
				try {
					fvalue = rst.getObject(fname);
				} catch (Exception e) {

				}
				m.invoke(t, fvalue);
			}
		}
		return t;
	}

	public static List<Emp> selectSplitAll(String kw, Integer cp, Integer ls) throws SQLException {
		List<Emp> list = new ArrayList<Emp>();
		// 对kw作空值判断
		if (kw == null) {
			kw = "";
		}
		kw = "%" + kw + "%";
		// 创键数据路连接对象
		Connection conn = ConnPoolUtil.getConnection();
		// 封装sql语句

		String sql = "select empno,ename,job,hiredate,sal,comm,mgr,deptno from emp" + " where ename like ? limit ?,?";

		// 创建sql发送对象
		PreparedStatement pst = conn.prepareStatement(sql);

		// 为占位符设置内容
		pst.setObject(1, kw);
		pst.setObject(2, (cp - 1) * ls);
		pst.setObject(3, ls);
		// 执行sql语句,获取的数据会存在resultSet中,一种数据表的结构,存在指针,初始位置在第一行数据上面
		ResultSet rst = pst.executeQuery();
		// 通过循环,使用next()方法将指针下移
		while (rst.next()) {
			list.add(new Emp(rst.getInt("empno"), rst.getString("ename"), rst.getString("job"), rst.getInt("mgr"),
					rst.getDate("hiredate"), rst.getDouble("sal"), rst.getDouble("comm"), rst.getInt("deptno")));

		}

		rst.close();
		pst.close();
		ConnPoolUtil.close(conn);

		return list;
	}

	public static int selectCount(Connection conn, String sql, Object... params) throws Exception {
		pst = conn.prepareStatement(sql);

		for (int i = 0; i < params.length; i++) {
			pst.setObject(i + 1, params[i]);
		}

		ResultSet rst = pst.executeQuery();
		if (rst.next()) {
			return rst.getInt(1);
		}

		return 0;
	}

	public static String upperCase(String column) {
		// 方法名第一个首字母大写+后面的字母
		return column.substring(0, 1).toUpperCase() + column.substring(1, column.length());
	}
}

数据库连接池

数据库连接池原理:
1.当系统启动【初始化的时候】会创建5【可设置】个数据库初始连接
2.当客户请求到来的时候【用户要获取Connection对象的时候】
2.1连接池中有空闲连接,直接将空闲的连接中的一个赋给客户使用
2.2如果连接池中没有空闲连接了,而且连接的数据量没有超过最大数量,
那么连接池会向数据库申请创建新的连接【5 可设置】,然后交给客户使用
2.3在2.2的情况下超过了最大连接数据,等待
3.当客户数据库操作完成后,这个连接怎么处理
3.1连接池中的空闲连接数已经超过了初始连接数,那么直接销毁
3.2连接池中的空闲连接数没有超过初始连接数据,那么进入空闲状态
3.3如果有等待的用户,那么这个连接直接交给等待的用户使用

数据库连接池的概念:
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

使用数据库连接池的流程:
在这里插入图片描述
使用数据库连接池的步骤:
第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。
优点:
较少了网络开销
系统的性能会有一个实质的提升
没有TIME_WAIT状态

使用数据库连接池需配置几项参数:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
最大空闲时间
获取连接超时时间
超时重试连接次数

常见的数据库连接池:
BDCP
C3P0
Druid
HikariCP

这里介绍下Druid的优点:
强大的监控特性,通过Druid提供的监控功能,可以清楚知道连接池和SQL的工作情况。
a. 监控SQL的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈信息;

b. SQL执行的耗时区间分布。什么是耗时区间分布呢?比如说,某个SQL执行了1000次,其中0-1毫秒区间50次,1-10毫秒800次,10-100毫秒100次,100-1000毫秒30次,1-10秒15次,10秒以上5次。通过耗时区间分布,能够非常清楚知道SQL的执行耗时情况;然后就可以对SQL语句进行优化

c. 监控连接池的物理连接创建和销毁次数、逻辑连接的申请和关闭次数、非空等待次数、PSCache命中率等。

方便扩展。Druid提供了Filter-Chain模式的扩展API,可以自己编写Filter拦截JDBC中的任何方法,可以在上面做任何事情,比如说性能监控、SQL审计、用户名密码加密、日志等等。
Druid集合了开源和商业数据库连接池的优秀特性,并结合阿里巴巴大规模苛刻生产环境的使用经验进行优化。

Druid的jar包下载连接:https://mvnrepository.com/artifact/com.alibaba/druid/1.0.20
以后对数据库的连接直接可以通过连接池对数据库连接进行管理,特别方便
举例代码如下

package com.zhouym.mvcpro.vo;

import java.sql.Connection;
import java.sql.SQLException;

import com.alibaba.druid.pool.DruidDataSource;

public class ConnPoolUtil {
	//创建数据库连接对象
	private static String URL = "jdbc:mysql://localhost:3306/demo?useSSL=true&useUnicode=true&characterEncoding=UTF-8";
	//创建驱动信息
	private static String DRIVER="com.mysql.jdbc.Driver";
	//创建数据库名称
	private static String USER="root";
	//创建数据库用户密码
	private static String PASSWORD="123456";
	//实例化一个数据源对象
	private static DruidDataSource dataSource = new DruidDataSource();
	
	//使用静态代码块为数据源对象初始化值
	static {
		//配置连接地址
		dataSource.setUrl(URL);
		//配置数据库用户名
		dataSource.setUsername(USER);
		//配置数据库用户密码
		dataSource.setPassword(PASSWORD);
		//记载驱动信息
		dataSource.setDriverClassName(DRIVER);
		//设置初始化连接大小
		dataSource.setInitialSize(10);
		//设置最大连接数
		dataSource.setMaxActive(20);
		//设置连接最大等待时间
		dataSource.setMaxWait(3000);
	}
	
	/**
	 * 和数据库建立连接
	 * @return 
	 */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 关闭连接,调用这个close方法,是将连接回收到连接池中
	 * @param conn 连接对象
	 */
	public static void close(Connection conn) {
		if (conn!=null) {
			try {
				//将连接回收到连接池中
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
}
  • 97
    点赞
  • 478
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值