新建库
新建表
插入点数据
先不用MVC模式写功能,来看下缺点是什么
新建一个空项目
选项项目使用的JDK
自己的IDEA总是要重启下
新建模块
因maven还没教
添加框架支持
添加些必要依赖 这里注意下,如果导入jar包不对可以重新导入下或者是jar包本身出了问题
添加页面
配置Tomcat
启动
请求地址 因现在没有后端
现在写后端 并测试
解决控制台输出乱码问题
System.out输出乱码
需要用到JDBC连接Mysql数据库
导入Mysql驱动jar包
java web项目 一般会在WEB-INF下有个lib用来放Tomcat内没有,外部引入的依赖
可以创建个lib目录 然后将jar包复制进来
具体代码
package com.bank.web;
import com.bank.exception.AppException;
import com.bank.exception.MoneyNotEnoughException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.*;
import java.util.Locale;
/**
* 在不使用MVC架构模式的前提下,完成银行账户转账
* 分析这个程序存在哪些问题?
* @author hrui
* @date 2023/8/31 23:06
*/
@WebServlet("/transfer")
public class AccountTransferServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取响应流对象
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
//获取前端传来的参数
String fromActno = req.getParameter("fromActno");
String toActno = req.getParameter("toActno");
double money = Double.parseDouble(req.getParameter("money"));
// System.out.println("转出账户:"+fromActno);
// System.out.println("转入账户:"+fromActno);
// System.out.println("转账金额:"+fromActno);
//编写转账的业务逻辑代码,连接数据库,进行转账操作
//1.转账之前要判断转出账户的余额是否充足
Connection conn=null;
PreparedStatement ps=null;
//PreparedStatement ps2=null;
ResultSet rs=null;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("xxx", "xxx", "xxx");
conn.setAutoCommit(false);
//获取执行对象
String sql="select balance from t_act where actno=?";
ps = conn.prepareStatement(sql);
ps.setString(1, fromActno);
//执行sql,返回结果集
rs = ps.executeQuery();
//获取结果集
if(rs.next()){
double balance = rs.getDouble("balance");
if(balance
1>负责了数据接收
2>负责了核心业务处理
3>负责了数据库表中的CRUD操作(Create(增) Retrieve(查) Update(改) Delete(删))
4>负责了数据的页面展示
引起的缺点:
代码复用性差 比如查余额,可以抽取出来,单独放到一个方法
代码复用性差的原因,没有职能分工,独立组件的概念,代码之间耦合度高,扩展力差
操作数据库代码和业务逻辑混杂在一起,容易出错,无法专注于业务逻辑书写
下面以MVC的模式对上面代码改进
1.系统为什么要分层?
希望专人干专事.各司其职.分工明确.这样可以降低耦合,扩展增强.复用增强
2.软件架构中,有一个非常著名的架构模式:MVC模式
M(Model:数据/业务) V(View:视图/展示) C(Controller:控制器/核心)
首先是对JDBC的封装
JDBC工具类
package com.bank.utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
* @author hrui
* @date 2023/9/1 10:24
*/
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 username=bundle.getString("username");
private static String password=bundle.getString("password");
//不让创建对象,原因工具类方法一般都静态的,无需外部创建对象
//为防止外部创建对象,构造私有化
private DBUtil(){
}
//DBUtil类加载时注册驱动
static{
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//没有使用连接池,直接创建的连接对象
public static Connection getConnection() throws SQLException {
Connection conn= DriverManager.getConnection(url,username,password);
return conn;
}
public static void close(Connection conn, Statement statement, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试连接
封装实体类
package com.bank.mvc;
import java.util.Objects;
/**
* 账户实体类,用于封装账户信息
* POJO对象
* 有的人也会把这种专门封装数据的对象,称为bean对象(javabean:咖啡豆)
* 有的人也会把这种专门分装数据的对象,称为领域模型对象.domain对象.
* pojo,bean,domain
* @author hrui
* @date 2023/9/1 15:34
*/
public class Account {
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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Account acount = (Account) o;
return id.equals(acount.id) && actno.equals(acount.actno) && balance.equals(acount.balance);
}
@Override
public int hashCode() {
return Objects.hash(id, actno, balance);
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
}
编写DAO层
package com.bank.mvc;
import com.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 负责Acount数据的增删改查
* 1.什么是DAO
* Data Access Object(数据访问对象)
* 2.DAO实际上是一种设计模式,属于JAVAEE的设计模式之一.(不是23种设计模式)
* 3.DAO只负责数据库CRUD,没有任何业务逻辑
* 4.没有任何业务逻辑,只负责表中数据增删改查,有一个特殊的称谓:DAO对象
* @author hrui
* @date 2023/9/1 12:39
*/
public class AccountDao {
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) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(conn,ps,null);
}
return count;
}
public int deleteByActno(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) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(conn,ps,null);
}
return count;
}
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=? where id=?";
ps=conn.prepareStatement(sql);
ps.setDouble(1, act.getBalance());
ps.setLong(2, act.getId());
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(conn,ps,null);
}
return count;
}
public Account selectBtActNo(String actno){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
Account account=new Account();
try {
conn=DBUtil.getConnection();
String sql="select id,actno,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");
String actno1=rs.getString("actno");
Double balance=rs.getDouble("balance");
account.setId(id);
account.setActno(actno1);
account.setBalance(balance);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(conn,ps,null);
}
return account;
}
public List selectAll(){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
List list=new ArrayList<>();
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");
Account account=new Account();
account.setId(id);
account.setActno(actno);
account.setBalance(balance);
list.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(conn,ps,null);
}
return list;
}
}
原先在Servlet里
1>负责了数据接收
2>负责了狠心业务处理
3>负责了数据库表中数据CRUD操作
4>负责了页面数据展示
现在已经一层层分离了
两个异常类
用AccountSAervice来做业务处理
具体业务处理代码
package com.bank.mvc;
import com.bank.exception.AppException;
import com.bank.exception.MoneyNotEnoughException;
/**
* Service:业务
* 专门处理Account的业务处理类
* 该类只专注业务处理
* 主要专注于MVC模式,不需要专注于取什么名字 xxx都可以
* @author hrui
* @date 2023/9/2 0:49
*/
public class AccountService {
private AccountDao accountDao=new AccountDao();
/**
* 完成转账的业务逻辑
* @param fromActno
* @param toActno
* @param money
*/
public void transfer(String fromActno,String toActno,double money) throws MoneyNotEnoughException, AppException {
//查询余额是否充足
Account fromAccount = accountDao.selectBtActNo(fromActno);
if(fromAccount.getBalance()
servlet里做的就是调度
package com.bank.mvc;
import com.bank.exception.AppException;
import com.bank.exception.MoneyNotEnoughException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 让AcountServlet做为Controller 调度员
* 负责调度其他组件
* @author hrui
* @date 2023/9/1 12:27
*/
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取前端传来的参数
String fromActno = req.getParameter("fromActno");
String toActno = req.getParameter("toActno");
double money = Double.parseDouble(req.getParameter("money"));
//调用业务方法处理业务(调度Model处理业务)
AccountService accountService=new AccountService();
try {
accountService.transfer(fromActno,toActno,money);
resp.sendRedirect(req.getContextPath()+"/success.jsp");
} catch (MoneyNotEnoughException e) {
e.printStackTrace();
resp.sendRedirect(req.getContextPath()+"/moneynoenough.jsp");
} catch (AppException e) {
e.printStackTrace();
resp.sendRedirect(req.getContextPath()+"/error.jsp");
}
}
}
三个页面显示
上面代码缺少事务控制,一般事务都在service进行控制
一般来说是在service层去开启事务 提交事务
这里事务管理,做的low的方式就是在业务层获取数据库连接对象,然后传进去,当然这样做的话,在DAO层就不要在finally中关闭事务了.传个null就行,在Service中开启事务,并在Service中提交事务,在Service中的finally中关闭连接
上代码
DAO
package com.bank.mvc;
import com.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 负责Acount数据的增删改查
* 1.什么是DAO
* Data Access Object(数据访问对象)
* 2.DAO实际上是一种设计模式,属于JAVAEE的设计模式之一.(不是23种设计模式)
* 3.DAO只负责数据库CRUD,没有任何业务逻辑
* 4.没有任何业务逻辑,只负责表中数据增删改查,有一个特殊的称谓:DAO对象
* @author hrui
* @date 2023/9/1 12:39
*/
public class AccountDao {
public int insert(Account act,Connection conn){
PreparedStatement ps=null;
int count=0;
try {
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) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public int deleteByActno(Long id,Connection conn){
PreparedStatement ps=null;
int count=0;
try {
String sql="delete from t_act where id=?";
ps=conn.prepareStatement(sql);
ps.setLong(1, id);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public int update(Account act,Connection conn){
PreparedStatement ps=null;
int count=0;
try {
String sql="update t_act set balance=? where id=?";
ps=conn.prepareStatement(sql);
ps.setDouble(1, act.getBalance());
ps.setLong(2, act.getId());
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public Account selectBtActNo(String actno,Connection conn){
PreparedStatement ps=null;
ResultSet rs=null;
Account account=new Account();
try {
String sql="select id,actno,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");
String actno1=rs.getString("actno");
Double balance=rs.getDouble("balance");
account.setId(id);
account.setActno(actno1);
account.setBalance(balance);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return account;
}
public List selectAll(Connection conn){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
List list=new ArrayList<>();
try {
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");
Account account=new Account();
account.setId(id);
account.setActno(actno);
account.setBalance(balance);
list.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return list;
}
}
Service
package com.bank.mvc;
import com.bank.exception.AppException;
import com.bank.exception.MoneyNotEnoughException;
import com.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Service:业务
* 专门处理Account的业务处理类
* 该类只专注业务处理
* 主要专注于MVC模式,不需要专注于取什么名字 xxx都可以
* @author hrui
* @date 2023/9/2 0:49
*/
public class AccountService {
private AccountDao accountDao=new AccountDao();
/**
* 完成转账的业务逻辑
* @param fromActno
* @param toActno
* @param money
*/
public void transfer(String fromActno,String toActno,double money) throws MoneyNotEnoughException, AppException {
//Service层控制事务
//开启事务(需要用到Connection对象)
Connection conn=null;
try {
conn = DBUtil.getConnection();
conn.setAutoCommit(false);
//查询余额是否充足
Account fromAccount = accountDao.selectBtActNo(fromActno,conn);
if(fromAccount.getBalance()
但上面代码有点low
如何解决Connection传参问题
用ThreadLocal
改造代码
DBUtil
package com.bank.utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
* @author hrui
* @date 2023/9/1 10:24
*/
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 username=bundle.getString("username");
private static String password=bundle.getString("password");
//不让创建对象,原因工具类方法一般都静态的,无需外部创建对象
//为防止外部创建对象,构造私有化
private DBUtil(){
}
//DBUtil类加载时注册驱动
static{
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static ThreadLocal local=new ThreadLocal<>();
//没有使用连接池,直接创建的连接对象
public static Connection getConnection() throws SQLException {
Connection conn=local.get();
if(conn==null) {
conn = DriverManager.getConnection(url, username, password);
local.set(conn);
}
return conn;
}
public static void close(Connection conn, Statement statement, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
//注意:关闭连接时候移除 Tomcat是支持线程池的,不从当前线程里移除 有可能下次别人拿到了,但是conn已经关闭
local.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
将AccountDao恢复原状,还是从DBUtil里取Connection,但是不需要再传参数,注意finally里面Connection照样传null,原因:还是需要在Service层进行事务控制
package com.bank.mvc;
import com.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 负责Acount数据的增删改查
* 1.什么是DAO
* Data Access Object(数据访问对象)
* 2.DAO实际上是一种设计模式,属于JAVAEE的设计模式之一.(不是23种设计模式)
* 3.DAO只负责数据库CRUD,没有任何业务逻辑
* 4.没有任何业务逻辑,只负责表中数据增删改查,有一个特殊的称谓:DAO对象
* @author hrui
* @date 2023/9/1 12:39
*/
public class AccountDao {
public int insert(Account act){
PreparedStatement ps=null;
int count=0;
try {
Connection 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) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public int deleteByActno(Long id){
PreparedStatement ps=null;
int count=0;
try {
Connection 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) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public int update(Account act){
PreparedStatement ps=null;
int count=0;
try {
Connection conn=DBUtil.getConnection();
String sql="update t_act set balance=? where id=?";
ps=conn.prepareStatement(sql);
ps.setDouble(1, act.getBalance());
ps.setLong(2, act.getId());
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return count;
}
public Account selectBtActNo(String actno){
PreparedStatement ps=null;
ResultSet rs=null;
Account account=new Account();
try {
Connection conn=DBUtil.getConnection();
String sql="select id,actno,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");
String actno1=rs.getString("actno");
Double balance=rs.getDouble("balance");
account.setId(id);
account.setActno(actno1);
account.setBalance(balance);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return account;
}
public List selectAll(){
PreparedStatement ps=null;
ResultSet rs=null;
List list=new ArrayList<>();
try {
Connection 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");
Account account=new Account();
account.setId(id);
account.setActno(actno);
account.setBalance(balance);
list.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
DBUtil.close(null,ps,null);
}
return list;
}
}
业务层
package com.bank.mvc;
import com.bank.exception.AppException;
import com.bank.exception.MoneyNotEnoughException;
import com.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Service:业务
* 专门处理Account的业务处理类
* 该类只专注业务处理
* 主要专注于MVC模式,不需要专注于取什么名字 xxx都可以
* @author hrui
* @date 2023/9/2 0:49
*/
public class AccountService {
private AccountDao accountDao=new AccountDao();
/**
* 完成转账的业务逻辑
* @param fromActno
* @param toActno
* @param money
*/
public void transfer(String fromActno,String toActno,double money) throws MoneyNotEnoughException, AppException {
try(Connection conn= DBUtil.getConnection()){
conn.setAutoCommit(false);
//查询余额是否充足
Account fromAccount = accountDao.selectBtActNo(fromActno);
if(fromAccount.getBalance()