1,JDBC (JAVA DataBase Connectivity : java 数据库连接)
2,如何连接数据库?
需要有一个 对应数据库的驱动jar 文件,实现Driver 接口注册,再通过DiverManger 类来获取数据库的连接。
比如:
数据库的驱动jar 包 (Mysql 对应: Mysql-connector-java-5.17.jar,不同的数据库对应不用的jar 包)放入到 web root/WEB-INF/lib/ 目录下面
3,DOS开启数据库服务的方法:
1,点击windos 键,在弹出的搜索框中,输入cmd,右键 “管理员身份运行” 打开2,输入 net start mysql ,这样就能开启mysql 服务了。
4,注册加载数据库驱动有下面三种方式
显示注册:
DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
还可以使用这两种方式:
Class.forName("com.mysql.jdbc.Driver").newInstance();
Class.forName("com.mysql.jdbc.Driver"); //我们查看源码会发现,执行forName 会调用newInstance() ,所以跟上面的方法一样。
5,对数据库的 增 删 改 查
当我们使用Statement (或者 PreparedStatement)对数据库的增删改查 ,
其中 增加,删除,修改 都是执行executeUpdate()方法 操作,
查询是 executeQuary() 方法的操作,
并且查询操作会返回一个结果集(ResultSet)。
6,Statement 和 PreparedStatement 的区别:
我们可以利用 Statement 来实现对数据库的增删改查,我们只需要组织出正确的sql 语句,就可以实现。
但是我们所写的sql 语句不可以使用参数代替。也就是(?)代替。
PreparedStatement 继承于 Statement 重写了Statement 的所有方法。
PreparedStatement 允许使用不完整的 sql 语句,空缺的值使用(?)代替,直到执行的时候,再输入进去就可以了。
使用 Statement :
Statement createStatement=conn.createStatement();int executeUpdate2 = createStatement.executeUpdate("insert into user(name,password) values('1','2')");
使用 preparedStatement://创建预编译语句对象
pstmt = conn.prepareStatement("insert into user(name,password) values(?,?)");//给4个占位符赋值
pstmt.setString(1, "ww");
pstmt.setString(2, "789");//执行SQL语句(增加记录)
int n = pstmt.executeUpdate();
7,实现对数据库的操作。
package demo;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;public classEx01 {/**
* 演示:JDBC访问数据库的操作步骤 查询*/
public static voidmain(String[] args) {
Connection conn= null;
Statement stmt= null;
ResultSet rs= null;try{//1、加载驱动程序
Class.forName("com.mysql.jdbc.Driver").newInstance();//2、通过DriverManager建立数据库连接 Driver 驱动
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user= "root";
String password= "111";
conn=DriverManager.getConnection(url, user, password);//3.通过Connection获取语句对象Statement (声明)
stmt =conn.createStatement();//4.使用语句对象执行SQL语句
rs = stmt.executeQuery("select * from user");//5.遍历结果集
while(rs.next()) {//取出当前行各个字段的值
int id = rs.getInt("id");
String name= rs.getString("name");
String pwd= rs.getString("password");
System.out.println(id + " " + name + " " +pwd);
}
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}finally {//一定会执行的代码块,无论是否发生异常//6.释放相关资源
if (rs != null) {try{
rs.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
rs= null;
}if (stmt != null) {try{
stmt.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
stmt= null;
}if (conn != null) {try{
conn.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
conn= null;
}
}
}
}
package demo;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;public classEx02 {/**
* 演示:预编译语句对象PreparedStatemet的使用*/
public static voidmain(String[] args) {
Connection conn= null;
PreparedStatement pstmt= null;try{
Class.forName("com.mysql.jdbc.Driver");
String url= "jdbc:mysql://127.0.0.1:3306/test";
String user= "root";
String password= "111";
conn=DriverManager.getConnection(url, user, password);
pstmt=conn
.prepareStatement("insert into user(name,password,email,birthday) values(?,?,?,?)");//给4个占位符赋值
pstmt.setString(1, "ww");
pstmt.setString(2, "789");
pstmt.setString(3, "ww@qq.com");
pstmt.setDate(4, Date.valueOf("2016-01-01"));//执行SQL语句(增加记录)
int n =pstmt.executeUpdate();if (n > 0) {
System.out.println("增加记录成功");
}
}catch(ClassNotFoundException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}finally{if (pstmt != null) {try{
pstmt.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
pstmt= null; }if (conn != null)try{
conn.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
pstmt = conn.prepareStatement("delete from user where id=?");
pstmt.setInt(1, id);int n =pstmt.executeUpdate();if(n>0){return true; //操作成功
}else{return false; //操作石板
}
pstmt = conn.prepareStatement("update user set name=?,password=?,email=?,birthday=? where id=?");
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getPassword());
pstmt.setString(3, user.getEmail());
pstmt.setDate(4, user.getBirthday());
pstmt.setInt(5, user.getId());int n =pstmt.executeUpdate();if(n>0){return true;
}else{return false;
}
8,JDBC 批处理SQL
可以使用 Statement 或者 PreparedStatement 对象来实现。
注意:
能够批量处理执行的SQL 必须是 INSTER UPDATE DELETE 等返回 int类型的SQL。
不能批量执行 SELECT 语句,会进行报错。另外批处理需要数据库的支持,可能有些数据库不支持。
例子一:使用Statement 批处理
package cn.edu.aynu.sushe.utils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;public classPichuliStatement {private staticConnection conntection;private staticStatement statement;public static voidmain(String[] args) {try{
conntection=JDBCUtils.getConntection();
statement=conntection.createStatement();for (int i = 0; i < 10; i++) {
String sql= "insert into user(name,password) values('a" +i+ "','aaa" + i + "')";
statement.addBatch(sql);//batch 批量
}//批量执行将每句sql 执行的结果返回为 int【】 数组
int[] executeBatch =statement.executeBatch();for (int i = 0; i < executeBatch.length; i++) {
System.out.println(executeBatch[i] + "");
}
}catch(Exception e) {
e.printStackTrace();
}finally{//关闭流
if (conntection != null) {try{
conntection.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}//关闭流
if (statement != null) {try{
statement.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
} }
}
}
数据库:
输出:
1
1
1
1
1
1
1
1
1
1
这个返回的int[] 当数组中的数据出现 大于 0 就代表这条sql 语句被执行成功了。如果小于0 就意味着sql 语句没有执行成功。
例子二:使用PreparedStatement 批处理
package cn.edu.aynu.sushe.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;public classpichuliPreparedStatement {public static voidmain(String[] args) {try{
Connection conntection=JDBCUtils.getConntection();
String sql= "insert into user(name,password) values(?,?)";PreparedStatement prepareStatement=conntection.prepareStatement(sql);for (int i = 0; i < 10; i++) {int index = 1;
prepareStatement.setString(1, "aa"+i);
prepareStatement.setString(2, "aa"+i);
prepareStatement.addBatch();
}int[] executeBatch =prepareStatement.executeBatch();for (int i = 0; i < executeBatch.length; i++) {
System.out.println(i+"haha");
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
数据库输出:
处理结果集(针对的就是执行select 语句拿到的ResultSet 对象)
package cn.edu.aynu.sushe.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;public classResultSetDemo01 {/**
* @param args*/
public static voidmain(String[] args) {
Connection conntection;try{
conntection=JDBCUtils.getConntection();
Statement createStatement=conntection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs= createStatement.executeQuery("select * from user");//光标指向第2行
rs.absolute(2);
System.out.println(rs.getInt("id"));//光标向上移动1行
rs.previous();
System.out.println(rs.getInt("id"));//光标相对向下移动2行
rs.relative(2);
System.out.println(rs.getInt("id"));//光标相对向上移动2行
rs.relative(-2);
System.out.println(rs.getInt("id"));//向下移动一行
rs.next();
System.out.println(rs.getInt("id"));
}catch(Exception e) {
e.printStackTrace();
} }
}
输出:
10,JDBC 高级应用
DAO 模式 和 JavaBean
DAO (数据库操作对象 DataBase Access Object)是JDBC 下常用的模式。
保存数据时,将java bean 的属性拆分成sql语句,并保存到数据库中。
读取数据时,将数据从数据库读出来,通过setXXX方法设置到javabean 中。
看来代码怎么组成:
javabean 对象: (省略 get set 方法)
package cn.edu.aynu.shuse.bean;public classUsers {private int id; //用户的id,自动增长,作为 linkman 的外键
private String username; //用户的姓名
private String password; //用户的密码
getXXX
setxxx
Dao 类:
package cn.edu.aynu.shuse.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.edu.aynu.shuse.bean.Users;
import cn.edu.aynu.shuse.utils.JDBCUtils;/**
* 对user表的数据操作,两方面{ (1)根据账户名,去查找密码 (2)注册添加用户的信息 }
*
* @author Administrator
**/
public classUsersDao {/**
* QueryRunner类简化了执行SQL语句的代码,它与ResultSetHandler组合在一起就能完成大部分的数据库操作,大大减少编码量。
* 针对不同的数据库操作,QueryRunner类提供的不同的方法。*/QueryRunner qr= newQueryRunner(JDBCUtils.getDataSource());/**
* (1)根据账户名,去查找密码
*
* @throws SQLException*/
public Users select(String name) throws SQLException {
String sql= "select * from user where username=?";
Users list= qr.query(sql, new BeanHandler(Users.class), name);returnlist;
}/**
* (2)注册添加用户的信息
*
* @throws SQLException*/
public voidadd(String name, String password) throws SQLException {
String sql= "insert into user(username,password) values(?,?)";
qr.update(sql, name, password);
}
}
11,JDBC 数据库连接池
为什么需要使用数据库连接池?
在JDBC编程中,每次创建和断开Connection对象都会消耗一定的时间和IO资源。
频繁地创建、断开数据库连接势必会影响数据库的访问效率,甚至导致数据库崩溃。
为了避免频繁的创建数据库连接,工程师们提出了数据库连接池技术。
什么是数据库连接池?
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的数据库连接,而不是重新建立。
怎么使用数据库连接池?
【1】获取数据库连接(Connection)
既然要连接数据库就应该获取数据库连接对象(Connection),那么我们通过使用JDBC提供了javax.sql.DataSource接口
(它负责与数据库建立连接,并定义了返回值为Connection对象的方法)
Connection getConnection()
Connection getConnection(String username,String password)来拿到我们的数据库连接对象
【2】获取数据库连接池对象(DataSource)
我们习惯性的把实现了javax.sql.DataSource接口的类称为数据源,顾名思义,数据源即数据的来源。
在数据源中存储了所有建立数据库连接的信息。
常用的数据源分为两种 :
1,DBCP
2,C3P0
DBCP是数据库连接池(DataBase Connection Pool)的简称,是Apache组织下的开源连接池实现,也是Tomcat服务器使用的连接池组件。
单独使用DBCP数据源时,需要在应用程序中导入两个jar包。commons-pool.jar包 commons-dbcp.jar包
(commons-dbcp.jar包中包含两个核心类,分别是BasicDataSourceFactory和BasicDataSource,它们都包含获取DBCP数据源对象的方法。)
C3P0是目前最流行的开源数据库连接池之一,它实现了DataSource数据源接口,支持JDBC2和JDBC3的标准规范,易于扩展并且性能优越,
著名的开源框架Hibernate和 Spring使用的都是该数据源。
需要了解C3P0中DataSource接口的实现类ComboPooledDataSource,它是C3P0的核心类,提供了数据源对象的相关方法。
【3】使用DBCP 数据源 获取数据库的连接
当使用DBCP数据源时,首先得创建数据源对象,数据源对象的创建方式有两种。
第一种:通过BasicDataSource类直接创建数据源对象,手动给数据源对象设置属性值,然后获取数据库连接对象。
第二种:通过读取配置文件创建数据源对象,使用BasicDataSourceFactory工厂类读取配置文件,创建数据源对象,然后获取数据库连接对象。(推荐使用)
第一种方式:类创建
package cn.edu.aynu.DBCP;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;public classleichuangjian { public static DataSource ds = null;static{//获取DBCP的数据源对象
BasicDataSource basicDataSource = newBasicDataSource();/** 【1】设置数据库的配置信息*/
//设置连接数据库的驱动名称basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
//设置连接数据库的路径
basicDataSource.setUrl("jdbc:mysql://localhost:3306/test");//设置数据库的登陆账号
basicDataSource.setUsername("root");//设置数据库的登录密码
basicDataSource.setPassword("111");/** 【2】设置数据库连接池的信息
**/
//设置数据库连接池初始化的连接数目
basicDataSource.setInitialSize(10);//设置数据库连接池最大活跃的连接数目
basicDataSource.setMaxActive(5);//设置数据库连接池最小闲置的连接数目
basicDataSource.setMinIdle(2);
ds=basicDataSource;
}/**
* @param args
* @throws SQLException*/
public static voidmain(String[] args) throws SQLException {//导入jar包//从数据库获取一个连接
Connection connection =ds.getConnection();
System.out.println(connection);
}
}
第二种:使用配置文件进行创建
注意:这个配置文件 后缀必须是 : xxx.properties 否者创建的就是错误的
package cn.edu.aynu.DBCP;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;public classpeizhichuangjian {/**
* @param args
* @throws Exception*/@SuppressWarnings("static-access")public static voidmain(String[] args) throws Exception {//通过配置文件来返回数据库的连接
Properties properties = newProperties();//通过类加载器拿到配置信息
InputStream inputStream = new peizhichuangjian()// .getClass()// .getClassLoader()// .getSystemResourceAsStream("ds.properties");//加载拿到的流
properties.load(inputStream);//通过工厂类创建DataSourse
DataSource createDataSource =BasicDataSourceFactory
.createDataSource(properties);
Connection connection=createDataSource.getConnection();
System.out.println(connection);
}
}
【4】使用C3P0 数据源 获取数据库的连接
当使用C3P0数据源时,首先得创建数据源对象,数据源对象的创建方式有两种。
第一种:通过ComboPooledDataSource类直接创建数据源对象,手动给数据源对象设置属性值,然后获取数据库连接对象。
第二种:通过读取配置文件创建数据源对象,使用ComboPooledDataSource读取配置文件,创建数据源对象,然后获取数据库连接对象。(推荐使用)
(其中读取配置文件,有两种方式:一种是默认的,一种是自定义的详情看下面)
一,类创建:
package cn.edu.aynu.C3P0;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;public classleichuangjian {public static DataSource ds = null;static{
ComboPooledDataSource comboPooledDataSource= newComboPooledDataSource();//【1】配置数据源的信息
try{
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource
.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("111");//设置连接池信息
comboPooledDataSource.setInitialPoolSize(10);
comboPooledDataSource.setMaxPoolSize(100);
ds=comboPooledDataSource;
}catch(PropertyVetoException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}/**
* @param args
* @throws SQLException*/
public static voidmain(String[] args) throws SQLException {
Connection connection=ds.getConnection();
System.out.println(connection);
}
}
二,使用配置文件进行创建:
以xml 的方式进行的存储 这个文件存放在 src 目录下
其中的配置代码:
jdbc:mysql://127.0.0.1:3306/test
com.mysql.jdbc.Driver
root
111
3
10
2
10
jdbc:mysql://localhost:3306/test
com.mysql.jdbc.Driver
root
111
3
10
2
10
获取数据源:
package cn.edu.aynu.C3P0;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;public classmorenpeizhi {public static DataSource ds = null;static{//使用默认的配置
ComboPooledDataSource comboPooledDataSource = newComboPooledDataSource();
ds=comboPooledDataSource;//使用mysql-config 命名的文件创建
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql-config");
ds=comboPooledDataSource ;
}/**
* @param args
* @throws SQLException*/
public static voidmain(String[] args) throws SQLException {
Connection connection= ds.getConnection(); //拿到连接
System.out.println(connection);
}
}
12,事务实例: 银行转账(经典题目)
如何使用JDBC处理事务?
(1)在数据库操作中,一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元
(2)只有当事务中的所有操作都正常完成,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被撤销
例如 银行的转账业务
通过 Connection 接口中提供了三个方法来针对 JDBC 处理事物。
其中有三个方法:
setAutoCommit(boolean autoCommit)// 设置自动提交 我们需要将这个方法设置为 false 不自动提交
//开启事务
public static voidstartTransaction(){try{//获取连接
Connection conn =getConnection();//开启事务
conn.setAutoCommit(false);
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
commit() // 提交 当我们的事务完成的时候,提交
//提交事务
public static voidcommit(){try{//从集合tc中得到一个连接
Connection conn = tc.get();if(conn != null){
conn.commit();
}
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
rollback() // 回滚 当我们的事务出现异常的时候,回滚
//回滚事务
public static voidrollback(){try{//从集合tc中得到一个连接
Connection conn = tc.get();if(conn != null){
conn.rollback();
}
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
银行转账的业务逻辑
用户 zs 要向 ls 的账户上转200 元钱,该转账业务需要分成两步:
第一步:zs 的账户上需要减少200元钱
第二步:ls 的账户上需要增加200元钱
这两步操作需要都成功,事务才成功。也就是说,要么都执行成功,要么都没有执行成功。
那么就会出现两种结果,一种是commit() 执行成功,要么就需要执行 rollback () ;
13,介绍ThreadLocaL类
为什么使用ThreadLocaL 类?
Web应用程序,多用户并发访问。
一个客户端访问时,服务器启动一个线程,比如转账,在这个线程中独立完成这个事务操作,
另外一个客户端也来访问时,服务器也启动另外一个线程,在另外一个线程中独立完成转账事务操作。
要想多线程中,每个事务执行成功,必须保证每个线程有独立的Connection对象。
ThreadLocaL类介绍:
ThreadLocaL类:在一个线程中记录本地变量。
我们可以将一个Connection对象放到这个ThreadLocaL集合中,只要是这个线程中的对象都可以共享这个Connection对象,线程结束后,删除这个Connection对象。
这样保证:一个事务一个连接。
TreadLoacl本质上是一个Map,键是当前线程对象,值可以是我们的当前连接对象。
//创建一个ThreadLoacl对象,用当前线程作为key
private static ThreadLocal tc = new ThreadLocal();
14,实例
在使用数据源(DataSource )时,如果是业务处理,使用C3P0 时,不需要拿数据源对象。
Java Web应用开发的三层架构:
1、表示层(Web层):jsp和servlet2、业务逻辑层(Service层):XXXService,事务操作是在service层完成的。3、数据访问层(dao层):XXXDao
按照编写正常代码演示:
bean-->Utils ---> Dao--->service--->Servlet
先看下数据库表:
test数据库中 account 表三个字段: id 主键自动递增,name 姓名 , money 账户的金额 (double 类型)
Account.java
package cn.edu.aynu.rjxy.bean;public classAccount {private intid;privateString name;private doublemoney;public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}public doublegetMoney() {returnmoney;
}public void setMoney(doublemoney) {this.money =money;
}
}
JDBCUtils.java
package cn.edu.aynu.rjxy.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;public classJDBCUtils {//创建一个ThreadLoacl对象,用当前线程作为key
private static ThreadLocal tc = new ThreadLocal();//从c3p0-config.xml文件中读取默认配置创建数据库连接池对象
private static DataSource ds = newComboPooledDataSource();//得到数据库连接池对象
public staticDataSource getDataSource(){returnds;
}//从连接池中获取连接
public staticConnection getConnection() throws Exception{//从集合tc中获取一个Connection
Connection conn = tc.get();if(conn == null){
conn=ds.getConnection();//将conn存放到集合tc中
tc.set(conn);
System.out.println("首次创建连接:"+conn);
}returnconn;
}//开启事务
public static voidstartTransaction(){try{//获取连接
Connection conn =getConnection();//开启事务
conn.setAutoCommit(false);
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}//提交事务
public static voidcommit(){try{//从集合tc中得到一个连接
Connection conn = tc.get();if(conn != null){
conn.commit();
}
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}//回滚事务
public static voidrollback(){try{//从集合tc中得到一个连接
Connection conn = tc.get();if(conn != null){
conn.rollback();
}
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}//关闭连接释放资源
public static voidclose(){//从集合tc中得到一个连接
Connection conn = tc.get();if(conn != null){try{
conn.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}finally{//从集合tc中移除连接对象
tc.remove();
conn= null;
}
}
}
}
AccountDao,java
package cn.edu.aynu.rjxy.dao;
import java.sql.Connection;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.edu.aynu.rjxy.bean.Account;
import cn.edu.aynu.rjxy.utils.JDBCUtils;public classAccountDao {//根据账户名查询账户信息
publicAccount find(String name) throws Exception{
QueryRunner run= new QueryRunner(); //处理事务,不需要传 数据源对象
Connection conn =JDBCUtils.getConnection();
System.out.println("find()得到连接:"+conn);
String sql= "select * from account where name=?";
Account account= (Account)run.query(conn, sql, new BeanHandler(Account.class), newObject[]{name});returnaccount;
}//转账(可能转入也可能转出)
public voidupdate(Account account) throws Exception {
QueryRunner run= newQueryRunner();
Connection conn=JDBCUtils.getConnection();
System.out.println("update()得到连接:"+conn);
String sql= "update account set money=? where name=?";
run.update(conn, sql,newObject[]{account.getMoney(),account.getName()});
}
}
AccountService.java 转账的业务逻辑
package cn.edu.aynu.rjxy.service;
import cn.edu.aynu.rjxy.bean.Account;
import cn.edu.aynu.rjxy.dao.AccountDao;
import cn.edu.aynu.rjxy.utils.JDBCUtils;public classAccountService {//转账事务
public static void transfer(String fromName,String toName,doublebalance){try{//开启事务
JDBCUtils.startTransaction();
AccountDao dao= newAccountDao();//查询两个账户的金额
Account accountFrom =dao.find(fromName);
Account accountTo=dao.find(toName);//判断是否可以转账
if(balance
accountFrom.setMoney(accountFrom.getMoney()-balance);//设置转入账户的金额
accountTo.setMoney(accountTo.getMoney()+balance);//执行数据库更改操作
dao.update(accountFrom);
dao.update(accountTo);//提交事务
JDBCUtils.commit();
System.out.println("事务提交成功");
}else{
System.out.println("转出账户金额不足");
}
}catch(Exception e) {//回滚事务
JDBCUtils.rollback();
System.out.println("事务提交失败");
e.printStackTrace();
}finally{//释放资源
JDBCUtils.close();
}
}
}
测试转账是否能够完成
import cn.edu.aynu.rjxy.service.AccountService;public classTestTransfer {/**
* @param args*/
public static voidmain(String[] args) {//实现从zs账户上转账200到ls账户上
AccountService.transfer("zs", "ls", 200);
}
}
源码:https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89
数据库:https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89