模拟银行转账(java+mysql+tomcat +JDBC+ druid连接池 + Servlet + Ajax)
项目准备前提
1.准备数据表
2.准备实验数据
3.jar包准备
4.准备好JDBC工具类
1.准备数据表
创建用户数据表,所需字段有:用户id, 用户姓名, 用户密码, 用户金额.
CREATE TABLE `account` (
`aid` int(11) NOT NULL AUTO_INCREMENT,
`uname` varchar(255) DEFAULT NULL,
`upwd` varchar(255) DEFAULT NULL,
`balance` decimal(8,2) DEFAULT NULL,
PRIMARY KEY (`aid`)
) ENGINE=InnoDB;
一个简单数据表创建就完成了
2.准备实验数据
INSERT INTO `account` VALUES (1, 'tesDest','112342', 1200.00);
INSERT INTO `account` VALUES (2, 'segvcw', '223352',680.50);
这边我们插入两条实验数据,用于模拟两个用户.
3.jar包准备
以下是我自己使用的jar包,分别是在项目下的lib目录下和web的项目leb目录下
注意 : 因为这边是使用web项目所以要先给项目加入web框架
3.1 加入web框架
选中项目文件夹右键, 点击如图选项:
勾选如图中的选项,后点击ok
4.准备JDBC工具类
我们使用JDBC技术时常需要进行许多的重复操作,于是我们可以将一些操作打包成工具类. 方便我们后面的操作.
package com.huawei.util;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类【加入事务控制 + 线程 + Druid连接池】
*/
public class DBUtils {
static String USER=null;
static String USER_PASS=null;
static String DB_URL=null;
static String DRIVER_CLASS=null;
private static DataSource dataSource;
//容器变量 [ThreadLocal用于隔离多线程并发 产生的数据混乱]
private static ThreadLocal<Connection> local = new ThreadLocal<>();
/**
* 静态代码块加载属性配置文件
*/
static {
Properties config=new Properties();
try {
//找到文件
//InputStream is= JdbcUtil.class.getResourceAsStream("/db.properties");
InputStream is= DBUtils.class.getClassLoader().getResourceAsStream("db.properties");
//并读取到内存
config.load(is);
//读取属性数据的key
//DB_URL = config.getProperty("jdbc.url");
//USER = config.getProperty("jdbc.username");
//USER_PASS = config.getProperty("jdbc.password");
DRIVER_CLASS = config.getProperty("jdbc.driverClassName");
//初始化Durid连接池数据源
DruidDataSource dds = new DruidDataSource();
dds.configFromPropety(config);//加载属性文件到数据源对象
dataSource = dds;
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return dataSource;
}
/**
* 注册驱动
*/
static {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取链接对象
* @return
*/
public static Connection getConn(){
if(local.get()==null) {//当前线程是否有连接对象
Connection conn = null;
try {
//conn = DriverManager.getConnection(DB_URL, USER, USER_PASS);
conn = dataSource.getConnection();
local.set(conn);//放入当前线程对象中
} catch (Exception e) {
e.printStackTrace();
}
//return conn;
}
return local.get();
}
/***************事务控制的方法********************/
public static void beginTransaction() {
try {
getConn().setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public static void commit(){
try {
getConn().commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public static void rollback(){
try {
getConn().rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
/**
* 资源释放方法
* @param conn
* @param stmt
* @param rs
*/
public static void close(Connection conn,Statement stmt,ResultSet rs){
if(rs!=null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.setAutoCommit(true);//设置回默认状态
conn.close();
local.remove();//清空当前线程中的数据,防止内存泄漏
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 通用的DML sql语句执行方法
* @param sql
* @param params
* @return
*/
public static int update(String sql,Object... params) throws Exception{
int result=0;
Connection conn = getConn();
PreparedStatement psmt= conn.prepareStatement(sql);
//获取参数化SQL中的?个数
int count= psmt.getParameterMetaData().getParameterCount();
if(count != params.length){
return -1;
}
//遍历参数个数,从动态数组中取值注入
for (int i=1;i<=count;i++){
psmt.setObject(i,params[i-1]);
}
//执行SQL
result = psmt.executeUpdate();
close(conn,psmt,null);
return result;
}
public static ResultSet query(String sql,Object... params) throws Exception{
ResultSet result=null;
Connection conn = getConn();
PreparedStatement psmt= conn.prepareStatement(sql);
//获取参数化SQL中的?个数
int count= psmt.getParameterMetaData().getParameterCount();
if(count != params.length){
return null;
}
//遍历参数个数,从动态数组中取值注入
for (int i=1;i<=count;i++){
psmt.setObject(i,params[i-1]);
}
//执行SQL
result = psmt.executeQuery();
return result;
}
}
数据库要有配置文件db.properties 放在src目录下
druid.url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
druid.username=root
druid.password=root
druid.driverClassName=com.mysql.jdbc.Driver
druid.maxActive=8
druid.maxWait=5000
项目开始
前端部分
创建表单,设定处理方法的servlet以及处理方法.
action="/transfer.do" method="post"
<h3>模拟银行转账</h3>
<form action="/transfer.do" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr width="100px">
<td>转账人</td>
<td><input name="fromAc"> </td>
</tr>
<tr>
<td>转账金额</td>
<td><input name="money"> </td>
</tr>
<tr>
<td>收款人</td>
<td><input name="toAc"></td>
</tr>
<tr colspan="2"><input type="submit" value="提交"></tr>
</table>
</form>
后端部分
创建实体账户类(根据数据库表中的字段创建属性)
然后生成get/set方法,以及toString方法
我的数据表(如图):
我的数据库为my, 记住数据库引擎必须是InnoDB. 不然无法使用事务
package com.huawei.entity;
/**
* 账户实体类
*/
public class Account {
private int aid;
private String uname;
private String upwd;
private double balance;
public int getAid() {return aid; }
public void setAid(int aid) {this.aid = aid;}
public String getUname() {return uname;}
public void setUname(String uname) {this.uname = uname;}
public String getUpwd() {return upwd;}
public void setUpwd(String upwd) {this.upwd = upwd;}
public double getBalance() {return balance;}
public void setBalance(double balance) {this.balance = balance;}
@Override
public String toString() {
return "Account{" +
"aid=" + aid +
", uname='" + uname + '\'' +
", upwd='" + upwd + '\'' +
", balance=" + balance +
'}';
}
}
创建TransactionDao类, 写基本操作方法
public class TransactionDao{
public QueryRunner runner = null;
public TransactionDao() {
this.runner = new QueryRunner();
}
//转入方法
public void transationIn(String name1, double money){
//TODO
String sql = "update account set balance = balance+? where uname=?";
try {
//在dao持久层每做一个操作 都从当前线程对象进行操作
runner.update(DBUtils.getConn(),sql,money,name1);
} catch (SQLException e) {
e.printStackTrace();
}
}
//转出方法
public void transationOut(String name1, double money){
String sql = "update account set balance = balance-? where uname=?";
try {
runner.update(DBUtils.getConn(),sql,money,name1);
} catch (SQLException e) {
e.printStackTrace();
}
}
//查询方法
public Account queryAccountByWhere(String accOn){
Account account = null;
String sql ="select * from account where uname=?";
try {
account = runner.query(DBUtils.getConn(),sql,accOn,new BeanHandler<>(Account.class));
} catch (SQLException e) {
e.printStackTrace();
}
return account;
}
}
创建业务类TransactionService, 实现业务
public class TransactionService{
private TransactionDao tranDao;
public TransactionService() {
this.tranDao = new TransactionDao();
}
//确认账户是否存在
public boolean chageAccount(String accNo){
//TODO
boolean falg = false;
Account account = tranDao.queryAccountByWhere(accNo);
if (account !=null){
falg = true;
}
return falg;
}
//确认账户余额是否足够
public boolean balanceVerify(String accNo,double balance){
//TODO
boolean falg = false;
Account account = tranDao.queryAccountByWhere(accNo);
//如果为空返回失败
if (account==null){
return falg;
//如果金额不够返回失败
}else if (account.getBalance() <balance){
return falg;
}else {
falg = true;
}
return falg;
}
//转账方法
public boolean transactionAccount(String accNo,String toAccount,double balance){
boolean falg = false;
try {
//转入和转出同时进行
tranDao.transationOut(accNo,balance);
// int i = 1/0;
tranDao.transationIn(toAccount,balance);
//没出问题就为成功,否则失败
falg=true;
} catch (Exception e) {
e.printStackTrace();
}
return falg;
}
}
创建servlet类
注解中 value 的值一定要与表单中的 action 的值一致不然无法找到
@WebServlet(value = "/transfer.do")
public class TtansferServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean isFlag = false;
//1.接收表单参数
String fromAc = req.getParameter("fromAc");
String toAc = req.getParameter("toAc");
String moneyStr = req.getParameter("money");
double money = 0.0;
if (moneyStr!=null && !"".equals(moneyStr)){
money = Double.parseDouble(moneyStr);
}
//2.创建业务层对象 调用业务层方法
TransactionService tranService = new TransactionService();
PrintWriter out = resp.getWriter();
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
if (tranService.chageAccount(fromAc)){
if (tranService.chageAccount(toAc)){
if (tranService.balanceVerify(fromAc,money)){
//3.根据业务方法的返回值进行处理
boolean iFlag= tranService.transactionAccount(fromAc,toAc,money);
if(iFlag) {
System.out.println("转账成功!");
//以响应输出成功标志到前端
out.println("<h3>转账成功</h3>");
}else {
System.out.println("转账失败!");
//以响应输出失败标志到前端
out.println("<h3>转账失败</h3>");
throw new RuntimeException("转账失败");
}
}else {
out.println("<h3>"+fromAc+"账户余额不足</h3>");
}
}else {
out.println("<h3>"+toAc+"账户不存在</h3>");
}
}else {
out.println("<h3>"+fromAc+"账户不存在</h3>");
}
}
}
创建过滤器
注解中的值是用于指定过滤哪一些servlet, 并且执行事务.
@WebFilter("/*")
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
System.out.println("-------------------请求执行---------------");
DBUtils.beginTransaction();//开启
filterChain.doFilter(servletRequest, servletResponse);
DBUtils.commit();// 提交事务
System.out.println("------------------给出响应---------------");
} catch (Exception e) {
DBUtils.rollback();//回滚事务
e.printStackTrace();
}
}
@Override
public void destroy() {
}
}
配置Tomcat
点击这个地方
按照顺序选择这两个
1.+
2.local
1.选择版本
2.自动生成项目
1.访问路径,建议为如图
2.点击Apply ok
然后启动: 在表单操作进行提交 控台返回结果
之后再查看数据库的数据