参考动力节点老杜讲的MVC写的笔记,如有错误,还请指正!
分析以下AccountTransferServlet他都负责了什么?
缺点
MVC架构模式【横向】
对mvc的理解?【面试题】
mvc是一种软件架构模式
M指Model,模型
完成具体的业务操作,包括处理业务以及处理数据
V指View,视图
使用JSP、html完成页面展示
C指Controller,是核心控制器
负责获取View的请求,并调用模型将数据交给View展示
使用mvc架构处理用户请求的具体过程
使用mvc架构的优点
Data Access Object,数据访问对象
DAO实际上是一种设计模式,属于JavaEE的设计模式之一(不是23种设计模式)
DAO只负责数据库表的CRUD,没有任何业务逻辑在里面
一般情况下,一张表对应一个DAO对象
package com.st.bank.dao.impl;
import com.st.bank.dao.AccountDao;
import com.st.bank.pojo.Account;
import com.st.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* @author: TIKI
* @Project: mvc -AccountDaoImpl
* @Pcakage: com.st.bank.dao.Impl.AccountDaoImpl
* @Date: 2022年10月23日 19:27
* @Description:负责account数据的增删改查
*/
public class AccountDaoImpl implements AccountDao {
/** 插入账户信息
* @param act 账户信息
* @return 1表示插入成功
*/
public int insert(Account act){
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into t_act(actno,balance) values(?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1,act.getActno());
ps.setDouble(2,act.getBalance());
count= ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
/** 根据主键删除账户
* @param id 主键
* @return 删除成功返回1
*/
public int deleteById(Long id){
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "delete from t_act where id = ?";
ps = conn.prepareStatement(sql);
ps.setLong(1,id);
count = ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
/** 更新账户
* @param act
* @return
*/
public int update(Account act){
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update t_act set balance = ?, actno = ? where id=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1,act.getBalance());
ps.setString(2,act.getActno());
ps.setLong(3,act.getId());
count = ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
/** 根据账号查询账户
* @param actno
* @return
*/
public Account selectByActno(String actno){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Account act = null;
try {
conn = DBUtil.getConnection();
String sql = "select id, balance from t_act where actno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1,actno);
rs = ps.executeQuery();
if (rs.next()){
Long id = rs.getLong("id");
Double balance = rs.getDouble("balance");
act = new Account(id,actno,balance);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,rs);
}
return act;
}
/** 获取所有的账户
* @return
*/
public List<Account> selectAll(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
List<Account> list= null;
try {
conn = DBUtil.getConnection();
String sql = "select id, actno,balance from t_act";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()){
Long id = rs.getLong("id");
String actno =rs.getString("actno");
Double balance = rs.getDouble("balance");
list.add(new Account(id,actno,balance));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,rs);
}
return list;
}
}
DAO对象查到的数据如何返回给控制器进而显示到页面?
利用java语言面向对象的特点->将数据封装成一个对象返回给控制器
比如针对t_act设计Account类【pojo对象】
有的人也会把这种专门封装数据的对象,称为【bean对象】,或者称为领域模型对象【domain对象】
package com.st.bank.pojo;
/**
* @author: TIKI
* @Project: mvc -Accout
* @Pcakage: com.st.bank.mvc.Accout
* @Date: 2022年10月22日 19:21
* @Description:账号实体类
*/
public class Account {
// 一般属性不设计为基本数据类型,建议使用包装类(引用数据类型),防止查询数据为null带来的问题
private Long id;
private String actno;
private Double balance;
public Account() {
}
public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
一般类的属性不设计为基本数据类型,建议使用包装类(引用数据类型),防止查询数据为null带来的问题
service中编写纯业务代码,service中的方法名需要体现出处理的是什么业务
比如针对Account业务,编写AccountService业务
package com.st.bank.service.impl;
import com.st.bank.dao.AccountDao;
import com.st.bank.dao.impl.AccountDaoImpl;
import com.st.bank.exceptions.AppException;
import com.st.bank.exceptions.MoneyNotEnoughException;
import com.st.bank.pojo.Account;
import com.st.bank.service.AccountService;
import com.st.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author: TIKI
* @Project: mvc -AccountService
* @Pcakage: com.st.bank.service.AccountService
* @Date: 2022年10月23日 19:29
* @Description: 专门处理Account业务的一个类
* 在该类中应编写纯业务代码
*/
public class AccountServiceImpl implements AccountService {
// 为什么定义到这里?因为在每一个业务方法中都可以需要连接数据库。
private AccountDao accountDao = new AccountDaoImpl();
// 这里的方法起名,一定要体现出,你要处理的是什么业务。
// 我们要提供一个能够实现转账的业务方法(一个业务对应一个方法。)
/**
* 完成转账的业务逻辑
* @param fromActno 转出账号
* @param toActno 转入账号
* @param money 转账金额
*/
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
// service层控制事务
try (Connection connection = DBUtil.getConnection()){
System.out.println(connection);
// 开启事务(需要使用Connection对象)
connection.setAutoCommit(false);
// 查询余额是否充足
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
throw new MoneyNotEnoughException("对不起,余额不足");
}
// 程序到这里说明余额充足
Account toAct = accountDao.selectByActno(toActno);
// 修改余额(只是修改了内存中java对象的余额)
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
// 更新数据库中的余额
int count = accountDao.update(fromAct);
// 模拟异常
/*String s = null;
s.toString();*/
count += accountDao.update(toAct);
if (count != 2) {
throw new AppException("账户转账异常!!!");
}
// 提交事务
connection.commit();
} catch (SQLException e) {
throw new AppException("账户转账异常!!!");
}
}
}
在service中调用dao和view
事务一定是在service中实现的
package com.powernode.threadlocal;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义一个ThreadLocal类
*/
public class MyThreadLocal<T> {
/**
* 所有需要和当前线程绑定的数据要放到这个容器当中
*/
private Map<Thread, T> map = new HashMap<>();
/**
* 向ThreadLocal中绑定数据
*/
public void set(T obj){
map.put(Thread.currentThread(), obj);
}
/**
* 从ThreadLocal中获取数据
* @return
*/
public T get(){
return map.get(Thread.currentThread());
}
/**
* 移除ThreadLocal当中的数据
*/
public void remove(){
map.remove(Thread.currentThread());
}
}
package com.st.bank.utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
* @author: TIKI
* @Project: mvc -Dbutil
* @Pcakage: com.st.bank.utils.DBUtil
* @Date: 2022年10月22日 19:04
* @Description:数据库工具类
*/
public class DBUtil {
private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc");
private static String driver = bundle.getString("driver");
private static String url = bundle.getString("url");
private static String user = bundle.getString("user");
private static String password = bundle.getString("password");
// 不让创建对象,因为工具类中的方法都是静态的。不需要创建对象。
// 为了防止创建对象,故将构造方法私有化。
private DBUtil(){}
// DBUtil类加载时注册驱动
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 这个对象实际上在服务器中只有一个。
private static ThreadLocal<Connection> local = new ThreadLocal<>();
/**
* 这里没有使用数据库连接池,直接创建连接对象。
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
Connection conn = local.get();
if (conn == null) {
conn = DriverManager.getConnection(url, user, password);
local.set(conn);
}
return conn;
}
/**
* 关闭资源
* @param conn 连接对象
* @param stmt 数据库操作对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement stmt, ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
// 思考一下:为什么conn关闭之后,这里要从大Map中移除呢?
// 根本原因是:Tomcat服务器是支持线程池的。也就是说一个人用过了t1线程,t1线程还有可能被其他用户使用。
local.remove();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
思考一下:为什么conn关闭之后,这里要从大Map中移除呢?
根本原因是:Tomcat服务器是支持线程池的。也就是说一个人用过了t1线程,t1线程还有可能被其他用户使用。
三层架构是指表示层、业务逻辑层和持久化层
表示层/表现层/web层(UI)
直接与前端交互
业务逻辑层Service(BLL)
处理表现层转发过来的前端请求(具体业务)
将从持久层获取的数据返回到表现层
持久化层Dao(数据访问层)
直接操作数据库完成CRUD,并将获得的数据返回到上一层,即业务逻辑层
相关技术
JDBC
MyBatis
Hibernate(实现了JPA规范)
配置较复杂、效率较低
JPA
SpringData(实现了JPA规范)
ActiveJDBC
SSM框架包括Spring、 SpringMVC、MyBatis
Spring
Spring负责管理整个项目,负责整个项目所有对象的创建、初始化、销毁以及维护对象和对象之间的关系。
SpringMVC(搭建了MVC架构模式)
完成了三层架构中的表示层
MyBatis
作为持久化层的实现者,完成对数据库的增、删、改、查功能
不同功能的类放在不同的包下
层和层之间通过接口联系