JDBC 常见知识点(一)
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(new com.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 class Ex01 { /** * 演示:JDBC访问数据库的操作步骤 查询 */ public static void main(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 class Ex02 { /** * 演示:预编译语句对象PreparedStatemet的使用 */ public static void main(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
能够批量处理执行的SQL 必须是 INSTER UPDATE DELETE 等返回 int 类型的SQL。 不能批量执行 SELECT 语句,会进行报错。另外批处理需要数据库的支持,可能有些数据库不支持。
package cn.edu.aynu.sushe.utils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class PichuliStatement { private static Connection conntection; private static Statement statement; public static void main(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 class pichuliPreparedStatement { public static void main(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 class ResultSetDemo01 { /** * @param args */ public static void main(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(); } } }
输出:
9,分页显示功能
10,JDBC 高级应用
DAO 模式 和 JavaBean
DAO (数据库操作对象 DataBase Access Object)是JDBC 下常用的模式。
保存数据时,将java bean 的属性拆分成sql语句,并保存到数据库中。
读取数据时,将数据从数据库读出来,通过setXXX方法设置到javabean 中。
package cn.edu.aynu.shuse.bean; public class Users { 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 class UsersDao { /** * QueryRunner类简化了执行SQL语句的代码,它与ResultSetHandler组合在一起就能完成大部分的数据库操作,大大减少编码量。 * 针对不同的数据库操作,QueryRunner类提供的不同的方法。 */ QueryRunner qr = new QueryRunner(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>(Users.class), name); return list; } /** * (2)注册添加用户的信息 * * @throws SQLException */ public void add(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 class leichuangjian { public static DataSource ds = null ; static { // 获取DBCP的数据源对象 BasicDataSource basicDataSource = new BasicDataSource(); /* * 【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 void main(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 class peizhichuangjian { /** * @param args * @throws Exception */ @SuppressWarnings("static-access") public static void main(String[] args) throws Exception { // 通过配置文件来返回数据库的连接 Properties properties = new Properties(); // 通过类加载器拿到配置信息 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 class leichuangjian { public static DataSource ds = null; static { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); // 【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 void main(String[] args) throws SQLException { Connection connection = ds.getConnection(); System.out.println(connection); } }
二,使用配置文件进行创建:
以xml 的方式进行的存储 这个文件存放在 src 目录下
其中的配置代码:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 默认配置,当使用ComboPooledDataSource无参构造器时,使用的就是这个配置 --> <default-config> <!-- 基本配置 --> <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">111</property> <!-- 每次增量,当需要创建Connection对象时,一次创建几个 --> <property name="acquireIncrement">3</property> <!-- 当创建池对象后,池中应该有几个Connection对象 --> <property name="initialPoolSize">10</property> <!-- 池中最少Connection个数,如果少于这个值,就会创建Connection --> <property name="minPoolSize">2</property> <!-- 池中最大连接个数 --> <property name="maxPoolSize">10</property> </default-config> <!-- 命名配置,new ComboPooledDataSource("oralce-config")时,使用的就是这个配置 --> <named-config name="mysql-config"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">111</property> <property name="acquireIncrement">3</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">2</property> <property name="maxPoolSize">10</property> </named-config> </c3p0-config>
获取数据源:
package cn.edu.aynu.C3P0; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class morenpeizhi { public static DataSource ds = null; static{ // 使用默认的配置 ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); ds =comboPooledDataSource; // 使用mysql-config 命名的文件创建 ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql-config"); ds =comboPooledDataSource ; } /** * @param args * @throws SQLException */ public static void main(String[] args) throws SQLException { Connection connection = ds.getConnection(); // 拿到连接 System.out.println(connection); } }
12,事务实例: 银行转账(经典题目)
如何使用JDBC处理事务?
(1)在数据库操作中,一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元 (2)只有当事务中的所有操作都正常完成,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被撤销 例如 银行的转账业务
通过 Connection 接口中提供了三个方法来针对 JDBC 处理事物。
//开启事务 public static void startTransaction(){ try { //获取连接 Connection conn = getConnection(); //开启事务 conn.setAutoCommit(false); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
//提交事务 public static void commit(){ try { //从集合tc中得到一个连接 Connection conn = tc.get(); if(conn != null){ conn.commit(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
rollback() // 回滚 当我们的事务出现异常的时候,回滚
//回滚事务 public static void rollback(){ 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<Connection> tc = new ThreadLocal<Connection>();
14,实例
在使用数据源(DataSource )时,如果是业务处理,使用C3P0 时,不需要拿数据源对象。
1、表示层(Web层):jsp和servlet 2、业务逻辑层(Service层):XXXService,事务操作是在service层完成的。 3、数据访问层(dao层):XXXDao
Account.java
package cn.edu.aynu.rjxy.bean; public class Account { private int id; private String name; private double money; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { 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 class JDBCUtils { //创建一个ThreadLoacl对象,用当前线程作为key private static ThreadLocal<Connection> tc = new ThreadLocal<Connection>(); //从c3p0-config.xml文件中读取默认配置创建数据库连接池对象 private static DataSource ds = new ComboPooledDataSource(); //得到数据库连接池对象 public static DataSource getDataSource(){ return ds; } //从连接池中获取连接 public static Connection getConnection() throws Exception{ //从集合tc中获取一个Connection Connection conn = tc.get(); if(conn == null){ conn = ds.getConnection(); //将conn存放到集合tc中 tc.set(conn); System.out.println("首次创建连接:"+conn); } return conn; } //开启事务 public static void startTransaction(){ try { //获取连接 Connection conn = getConnection(); //开启事务 conn.setAutoCommit(false); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //提交事务 public static void commit(){ try { //从集合tc中得到一个连接 Connection conn = tc.get(); if(conn != null){ conn.commit(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //回滚事务 public static void rollback(){ try { //从集合tc中得到一个连接 Connection conn = tc.get(); if(conn != null){ conn.rollback(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //关闭连接释放资源 public static void close(){ //从集合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 class AccountDao { //根据账户名查询账户信息 public Account 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), new Object[]{name}); return account; } //转账(可能转入也可能转出) public void update(Account account) throws Exception { QueryRunner run = new QueryRunner(); Connection conn = JDBCUtils.getConnection(); System.out.println("update()得到连接:"+conn); String sql = "update account set money=? where name=?"; run.update(conn, sql, new Object[]{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 class AccountService { //转账事务 public static void transfer(String fromName,String toName,double balance){ try { //开启事务 JDBCUtils.startTransaction(); AccountDao dao = new AccountDao(); //查询两个账户的金额 Account accountFrom = dao.find(fromName); Account accountTo = dao.find(toName); //判断是否可以转账 if(balance<accountFrom.getMoney()){ //可以转账 //设置转出账户的金额 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 class TestTransfer { /** * @param args */ public static void main(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