JDBC

JDBC

      • JDBC
        • 一、概念
        • 二、核心思想
        • 三、驱动和接口
        • 四、基本操作
        • 五、ResultSet使用
        • 六、SQL注入
        • 七、PreparedStatement
        • 八、封装工具类
        • 九、封装DAO层
        • 十、Service层
        • 十一、三层架构
        • 十二、事务
          • 12.1 事务的基本使用
          • 12.2 ThreadLocal【经典面试题】
        • 十三、DAO的封装DaoUtils
        • 十四、连接池
        • 十五、DBUtils帮助类用法

JDBC

一、概念

JDBC (Java Database Connectivity )指使用Java连接数据库,对数据库实现CRUD操作。

二、核心思想

Java定义一套操作数据库的规范(接口),各数据库厂商去实现该接口,保证程序员在使用上的统一。

三、驱动和接口

API(类和接口):在java.sql包中

  • DriverManager类:获得数据库连接
  • Connection接口:连接的接口
  • Statement接口以及其子接口PreparedStatment接口:操作数据库语句
  • ResultSet接口:查询时返回的结果集
  • SQLException类:数据库操作过程中的异常处理

驱动:

  • ODBC:桥接,使用系统去连接数据库,能够通用的连接大部分数据库,但是性能不好,而且功能不完整。
  • JDBC:数据库厂商提供的直连驱动包。
    • mysql-connector-java-5.1.40.jar:连接MySQL5的驱动
    • mysql-connector-java-8.0.20.jar:连接MySQL8的驱动

四、基本操作

操作步骤:

  • 0、将驱动包导入

  • 1、加载驱动

  • 2、获得连接

  • 3、通过连接获得SQL的执行对象

  • 4、通过执行对象执行SQL

  • 5、处理执行结果

  • 6、释放资源

public class JobsDAO {
    public static void main(String[] args) {
        // 1、加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        final String url = "jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8";
        final String username = "root";
        final String password = "root";

        try (
                // 2、获得连接
                final Connection connection = DriverManager.getConnection(url, username, password);
                // 3、获取执行sql对象
                final Statement statement = connection.createStatement()
        ){
            System.out.println(connection);
//            final String sql = "INSERT INTO t_jobs VALUES ('2', 'BBB', 2000, 10000);";
//            // 4、执行SQL语句(更新操作,增删改),返回影响的行数
//            final int count = statement.executeUpdate(sql);
//            // 5、处理结果
//            if (count > 0){
//                System.out.println("执行成功");
//            }else{
//                System.out.println("执行失败");
//            }
            final String sql = "SELECT COUNT(1) FROM t_jobs";
            // 4、执行SQL语句(查询操作),返回一个多行多列的结果集
            final ResultSet resultSet = statement.executeQuery(sql);
            // 5、处理结果
            // 循环每一行
            while (resultSet.next()){
                // 获取当前行每一列的值
                // 可以通过列的序号来获取,也可以通过列名来获取,推荐使用名称,COUNT(1)这种名称不太好使用的,推荐使用别名
                final long count = resultSet.getLong(1);
                System.out.println(count);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
}

五、ResultSet使用

用来封装查询结果集。

注意:ResultSet是仅向前操作的对象,通过next方法向前操作,不能回退。

当移动到当前行时,使用getXxx()方法来获得某个列的值,方法的参数为列名或者序号(从1开始)。

public class JobsDAO {
    public static void main(String[] args) {
        // 1、加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        final String url = "jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8";
        final String username = "root";
        final String password = "root";

        try (
                // 2、获得连接
                final Connection connection = DriverManager.getConnection(url, username, password);
                // 3、获取执行sql对象
                final Statement statement = connection.createStatement()
        ){
            System.out.println(connection);
            final String sql = "SELECT COUNT(1) FROM t_jobs";
            // 4、执行SQL语句(查询操作),返回一个多行多列的结果集
            final ResultSet resultSet = statement.executeQuery(sql);
            // 5、处理结果
            // 循环每一行
            while (resultSet.next()){
                // 获取当前行每一列的值
                // 可以通过列的序号来获取,也可以通过列名来获取,推荐使用名称,COUNT(1)这种名称不太好使用的,推荐使用别名
                final long count = resultSet.getLong(1);
                System.out.println(count);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
}

六、SQL注入

利用拼接SQL字符串中的单引号,来注入关键字来实现非法操作。

例如:一个简单登录操作如下:

System.out.println(connection);
String u = "zhangsan";
String p = "123456";
final String sql = "SELECT COUNT(1) FROM stu WHERE username = '"+u+"' AND pwd = '"+p+"'";
// 4、执行SQL语句(查询操作),返回一个多行多列的结果集
final ResultSet resultSet = statement.executeQuery(sql);
// 5、处理结果
// 循环每一行
while (resultSet.next()){
    // 获取当前行每一列的值
    // 可以通过列的序号来获取,也可以通过列名来获取,推荐使用名称,COUNT(1)这种名称不太好使用的,推荐使用别名
    final long count = resultSet.getLong(1);
    System.out.println(count > 0);
}

上面的代码执行看似没有问题,但是当用户将密码输入sdadsd' OR '1' = '1,无论用户名是什么,都会显示登录成功。就是简单的SQL注入攻击。

七、PreparedStatement

PreparedStatement接口是Statement接口的子接口。

特点:

  • 预编译语句,效率高
  • 安全,避免SQL注入
  • 动态填充内容,可以重复使用
// ?表示占位符
final String sql = "INSERT INTO stu(name, username, pwd) VALUES (?, ?, ?);";
try (
    // 2、获得连接
    final Connection connection = DriverManager.getConnection(url, username, password);
    // 3、获取执行sql对象
    final PreparedStatement preparedStatement = connection.prepareStatement(sql)
){
    for (int i = 0; i < 5; i++) {
        // 设置参数
        preparedStatement.setString(1, "AAA" + i);
        preparedStatement.setString(2, "BBBB" + i);
        preparedStatement.setString(3, "CCCC" + i);
        // 4、执行SQL语句(更新操作,增删改),返回影响的行数
        final int count = preparedStatement.executeUpdate();
        // 5、处理结果
        if (count > 0){
            System.out.println("执行成功");
        }else{
            System.out.println("执行失败");
        }
    }
}catch (SQLException e){
    e.printStackTrace();
}

八、封装工具类

封装连接类:

public class DBConnection {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8",
                "root", "root");
    }
}

上面的内容,无法热修改,因为需要修改源代码,所以对于可能在上线后还需要修改的参数,应该使用配置文件。如下处理:

dbconfig.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8
jdbc.user=root
jdbc.password=root
    private final static Properties PROPERTIES = new Properties();
    static {
        try {
            // 读取并加载配置文件
            final InputStream inputStream = DBConnection1.class.getResourceAsStream("/dbconfig.properites");
            PROPERTIES.load(inputStream);
            Class.forName(PROPERTIES.getProperty("jdbc.driver"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(PROPERTIES.getProperty("jdbc.url"),
                PROPERTIES.getProperty("jdbc.user"), PROPERTIES.getProperty("jdbc.password"));
    }
}

九、封装DAO层

ORM:Object Relational Mapping对象关系映射。

是指将实体类的对象与数据库中的一行记录相对应。

DAO:Data Access Object数据访问对象,即数据库操作类的对象。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    private String username;
    private String pwd;
}
public interface StudentDAO {
    /**
     * 添加
     * @param student
     * @return
     */
    boolean save(Student student);

    /**
     * 删除
     * @param id
     * @return
     */
    boolean delete(int id);

    /**
     * 修改
     * @param student
     * @return
     */
    boolean update(Student student);

    /**
     * 根据id查询
     * @param id
     * @return
     */
    Student findById(int id);

    /**
     * 查询所有
     * @return
     */
    List<Student> findAll();
}
public interface Constants {
    String SQL_STUDENT_SAVE = "INSERT INTO stu(name, username, pwd) VALUES (?,?,?)";
    String SQL_STUDENT_UPDATE = "UPDATE stu SET name = ?, username = ?, pwd = ? WHERE id = ?";
    String SQL_STUDENT_DELETE = "DELETE FROM stu WHERE id = ?";
    String SQL_STUDENT_FIND_BY_ID = "SELECT id, name, username, pwd FROM stu WHERE id = ?";
    String SQL_STUDENT_FIND_ALL = "SELECT id, name, username, pwd FROM stu";
}
public class StudentDAOImpl implements StudentDAO {
    @Override
    public boolean save(Student student) {
        try(
                Connection connection = DBConnection.getConnection();
                final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_STUDENT_SAVE);
                ) {
            // 设置参数
            preparedStatement.setString(1, student.getName());
            preparedStatement.setString(2, student.getUsername());
            preparedStatement.setString(3, student.getPwd());
            return preparedStatement.executeUpdate() > 0;
        }catch (SQLException e){
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean delete(int id) {
        try(
                Connection connection = DBConnection.getConnection();
                final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_STUDENT_DELETE);
        ) {
            // 设置参数
            preparedStatement.setInt(1, id);
            return preparedStatement.executeUpdate() > 0;
        }catch (SQLException e){
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean update(Student student) {
        try(
                Connection connection = DBConnection.getConnection();
                final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_STUDENT_UPDATE);
        ) {
            // 设置参数
            preparedStatement.setString(1, student.getName());
            preparedStatement.setString(2, student.getUsername());
            preparedStatement.setString(3, student.getPwd());
            preparedStatement.setInt(4, student.getId());
            return preparedStatement.executeUpdate() > 0;
        }catch (SQLException e){
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Student findById(int id) {
        try(
                Connection connection = DBConnection.getConnection();
                final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_STUDENT_FIND_BY_ID);
        ) {
            // 设置参数
            preparedStatement.setInt(1, id);
            final ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                return new Student(
                        resultSet.getInt("id"),
                        resultSet.getString("name"),
                        resultSet.getString("username"),
                        resultSet.getString("pwd")
                        );
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Student> findAll() {
        List<Student> list = new ArrayList<>();
        try(
                Connection connection = DBConnection.getConnection();
                final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_STUDENT_FIND_ALL);
        ) {
            final ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                final Student student = new Student(
                        resultSet.getInt("id"),
                        resultSet.getString("name"),
                        resultSet.getString("username"),
                        resultSet.getString("pwd")
                );
                list.add(student);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
        return list;
    }
}

十、Service层

业务逻辑层,也写作Business(简写Biz)。

代表软件中的功能。

作用:一般情况下,一个业务就是一个方法。在业务中调用数据库操作。

十一、三层架构

  • 界面层View
  • 业务层Service
  • 数据访问层DAO

调用顺序:View -> Service -> DAO

作用:需要修改时,只需要考虑其中某一层。

高内聚,低耦合:将代码中相似或相同的内容,抽取出相似的部分,写到一起,其他的对他进行调用,称为高内聚。低耦合,是指降低代码中相互的依赖性。

包的名称方式:

  • entity:存放实体类
  • dao:数据库操作
  • service:业务逻辑
  • view:界面
  • util:帮助类

案例:学生信息管理系统

十二、事务

12.1 事务的基本使用

步骤:

1、connection.setAutoCommit(false);

2、connection.commit();在执行最后执行。

3、connection.rollback()在代码异常时执行。

注意:

  • 如果在DAO中获取了连接,然后在Service中也获取连接,那么在多个连接中无法实现事务处理。

要想实现事务处理,必须在同一个连接中。为了实现同一个连接,有以下几种处理方式:

  • 1、将连接作为参数传递,可以实现,但是会导致代码的侵入性。如果service调用service还是多个连接。会出错。
  • 2、将连接存在一个公共位置,谁用谁取。看似可行,其实有线程安全问题,而且多线程操作名字相同会覆盖。
  • 为了解决上面2的问题,又要实现1的不足(耦合度高),系统提供一个类ThreadLocal。
12.2 ThreadLocal【经典面试题】

每个线程通过ThreadLocal绑定一个map对象。

当前线程中的所有流程步骤都共享该map对象、

// ThreadLocal源码
// 通过当前线程绑定的map设置值
public void set(T value) {
    // 获得当前线程
    Thread t = Thread.currentThread();
    // 根据当前线程获得该线程绑定的map
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 将对象设置到当前map中,key为当前ThreadLocal对象
        map.set(this, value);
    else
        // 如果没有,则通过该线程创建一个map
        createMap(t, value);
}

// 通过当前线程绑定的map获取值
public T get() {
    // 获得当前线程
    Thread t = Thread.currentThread();
    // 根据当前线程获得该线程绑定的map
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 通过key为当前ThreadLocal对象来获取存储的对象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 返回该对象
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
public class DBConnection {
    private static final Properties PROPERTIES = new Properties();
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();

    static {
        final InputStream inputStream = DBConnection.class.getResourceAsStream("/dbconfig.properties");
        try {
            PROPERTIES.load(inputStream);
            Class.forName(PROPERTIES.getProperty("jdbc.driver"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        // 在当前线程绑定的map中获取连接
        Connection connection = THREAD_LOCAL.get();
        if (connection == null){
            // 创建连接
            connection = DriverManager.getConnection(PROPERTIES.getProperty("jdbc.url"),
                    PROPERTIES.getProperty("jdbc.user"),
                    PROPERTIES.getProperty("jdbc.password"));
            // 设置到当前线程绑定的map中
            THREAD_LOCAL.set(connection);
        }
        return connection;
    }

    public static void closeAll(){
        // 在当前线程绑定的map中获取连接
        Connection connection = THREAD_LOCAL.get();
        if (connection != null){
            try {
                connection.close();
                // 在当前线程绑定的map中删除连接
                THREAD_LOCAL.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
public class AccountServiceImpl implements AccountService {
    private AccountDAO accountDAO = new AccountDAOImpl();
    @Override
    public void transfer(Account from, Account to) {
        Connection connection = null;
        try{
            connection = DBConnection.getConnection();
            connection.setAutoCommit(false);
            accountDAO.decrease(from);
//            int i = 5 / 0;
            accountDAO.increase(to);
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
            if (connection != null){
                try {
                    connection.rollback();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }finally {
            DBConnection.closeAll();
        }
    }
}
public class AccountDAOImpl implements AccountDAO {
    @Override
    public boolean increase(Account account) throws SQLException {
        Connection connection = DBConnection.getConnection();
        final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_ACCOUNT_INCREASE);
        preparedStatement.setInt(1, account.getMoney());
        preparedStatement.setString(2, account.getAccount());
        return preparedStatement.executeUpdate() > 0;
    }

    @Override
    public boolean decrease(Account account) throws SQLException {
        Connection connection = DBConnection.getConnection();
        final PreparedStatement preparedStatement = connection.prepareStatement(Constants.SQL_ACCOUNT_DECREASE);
        preparedStatement.setInt(1, account.getMoney());
        preparedStatement.setString(2, account.getAccount());
        return preparedStatement.executeUpdate() > 0;
    }
}

十三、DAO的封装DaoUtils

public interface RowMapper<T> {
    T getMapper(ResultSet resultSet) throws SQLException;
}
public class DAOUtils<T> {
    public boolean update(String sql, Object... args) throws SQLException {
        // 获得连接
        final Connection connection = DBConnection.getConnection();
        // 获得sql预编译操作对象
        final PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 设置参数
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i + 1, args[i]);
        }
        // 执行
        return preparedStatement.executeUpdate() > 0;
    }

    public List<T> queryList(String sql, RowMapper<T> mapper, Object... args) throws SQLException {
        List<T> list = new ArrayList<>();
        final Connection connection = DBConnection.getConnection();
        final PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i + 1, args[i]);
        }
        final ResultSet resultSet = preparedStatement.executeQuery();
        // 循环结果集
        while (resultSet.next()){
            // 封装结果集的行
            T t = mapper.getMapper(resultSet);
            // 添加到集合中
            list.add(t);
        }
        return list;
    }

    public T queryOne(String sql, RowMapper<T> mapper, Object... args) throws SQLException {
        final Connection connection = DBConnection.getConnection();
        final PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i + 1, args[i]);
        }
        final ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            T t = mapper.getMapper(resultSet);
            return t;
        }
        return null;
    }
}
public class StudentDAO {
    private DAOUtils<Student> daoUtils = new DAOUtils();

    public boolean save(Student student) throws SQLException {
        return daoUtils.update(Constants.SQL_STUDENT_SAVE,
                student.getName(), student.getSex(),
                student.getAge(), student.getPhone());
    }

    public boolean update(Student student) throws SQLException {
        return daoUtils.update(Constants.SQL_STUDENT_UPDATE,
                student.getName(), student.getSex(),
                student.getAge(), student.getPhone(), student.getId());
    }

    public boolean delete(int id) throws SQLException {
        return daoUtils.update(Constants.SQL_STUDENT_DELETE, id);
    }

    public List<Student> findAll() throws SQLException{
        return daoUtils.queryList(Constants.SQL_STUDENT_FIND_ALL, new RowMapper<Student>() {
            @Override
            public Student getMapper(ResultSet resultSet) throws SQLException {
                return new Student(
                        resultSet.getInt("id"),
                        resultSet.getString("name"),
                        resultSet.getString("sex"),
                        resultSet.getInt("age"),
                        resultSet.getString("phone")
                );
            }
        });
    }

    public Student findById(int id) throws SQLException{
        return daoUtils.queryOne(Constants.SQL_STUDENT_FIND_BY_ID, resultSet -> new Student(
                    resultSet.getInt("id"),
                    resultSet.getString("name"),
                    resultSet.getString("sex"),
                    resultSet.getInt("age"),
                    resultSet.getString("phone")
            )
        , id);
    }
}

十四、连接池

避免频繁创建和关闭连接。

连接池有很多种:DBCP、C3P0、druid(阿里出品)

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/stu_sys?useUnicode=true&characterEncoding=utf-8
username=root
password=root
#初始化连接数
initialSize=10
#最大活动连接数
maxActive=50
#最小空闲连接
minIdle=5
#最大等待时间5秒
maxWait=5000

上面的配置信息,key不能改,采用约定优于配置原则

约定优于配置:事先约定好key的名称,如果遵守约定,那么就无需配置,如果不遵守约定,就需要添加额外的配置。

public class DBConnection {
    private static final Properties PROPERTIES = new Properties();
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();
    private static DataSource dataSource; // 数据源连接池

    static {
        final InputStream inputStream = DBConnection.class.getResourceAsStream("/druid.properties");
        try {
            PROPERTIES.load(inputStream);
            dataSource = DruidDataSourceFactory.createDataSource(PROPERTIES);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDataSource(){
        return dataSource;
    }


    // dbutils类只有需要事务时才使用
    public static Connection getConnection() throws SQLException {
        Connection connection = THREAD_LOCAL.get();
        if (connection == null){
            connection = dataSource.getConnection();
            THREAD_LOCAL.set(connection);
        }
        return connection;
    }

    public static void closeAll() throws SQLException {
        Connection connection = THREAD_LOCAL.get();
        if (connection != null){
            // 在连接池中获取的连接,关闭时会还到池中再次使用
            connection.close();
            THREAD_LOCAL.remove();
        }
    }
}

十五、DBUtils帮助类用法

当没有事务时,可以直接使用:

private QueryRunner runner = new QueryRunner(DBConnection.getDataSource());

注意:结果集的封装,使用反射的方式需要字段名和属性名一致。

public class StudentDAOImpl implements StudentDAO {
    // DBUtils中的操作类的对象
    // 当没有事务时,直接使用连接池对象
    private QueryRunner runner = new QueryRunner(DBConnection.getDataSource());

    @Override
    public boolean save(Student student) throws SQLException {
        return runner.update(Constants.SQL_STUDENT_SAVE, student.getName(), student.getSex(),
                student.getAge(), student.getPhone()) > 0;
    }

    @Override
    public boolean update(Student student) throws SQLException {
        return runner.update(Constants.SQL_STUDENT_UPDATE, student.getName(), student.getSex(),
                student.getAge(), student.getPhone(), student.getId()) > 0;
    }

    @Override
    public boolean delete(int id) throws SQLException {
        return runner.update(Constants.SQL_STUDENT_DELETE, id) > 0;
    }

//    @Override
//    public List findAll() throws SQLException {
//        // 通过反射封装结果集,但是需要字段名与属性名一致
//        return runner.query(Constants.SQL_STUDENT_FIND_ALL, new BeanListHandler<>(Student.class));
//    }

    // 当字段名与属性不一致时,可以使用以下办法:
    // 1、当个别属性不一致时,可以通过别名的方式
    // 2、当很多属性不一致时,可以自己手动封装结果集
    @Override
    public List<Student> findAll() throws SQLException {
        // 通过反射封装结果集,但是需要字段名与属性名一致
        return runner.query(Constants.SQL_STUDENT_FIND_ALL, new ResultSetHandler<List<Student>>() {
            @Override
            public List<Student> handle(ResultSet resultSet) throws SQLException {
                List<Student> list = new ArrayList<>();
                while (resultSet.next()){
                    Student student = new Student(
                            resultSet.getInt("id"),
                            resultSet.getString("name1"),
                            resultSet.getString("sex"),
                            resultSet.getInt("age"),
                            resultSet.getString("phone")
                    );
                    list.add(student);
                }
                return list;
            }
        });
    }

    @Override
    public Student findById(int id) throws SQLException {
        return runner.query(Constants.SQL_STUDENT_FIND_BY_ID, new BeanHandler<>(Student.class), id);
    }
}

当有事务时:

public class AccountDAOImpl implements AccountDAO {
    private QueryRunner runner = new QueryRunner();

    @Override
    public boolean increase(Account account) throws SQLException {
        return runner.update(DBConnection.getConnection(),
                Constants.SQL_ACCOUNT_INCREASE,
                account.getMoney(), account.getAccount()) > 0;
    }

    @Override
    public boolean decrease(Account account) throws SQLException {
        return runner.update(DBConnection.getConnection(),
                Constants.SQL_ACCOUNT_DECREASE,
                account.getMoney(), account.getAccount()) > 0;
    }
}
public class AccountServiceImpl implements AccountService {
    private AccountDAO accountDAO = new AccountDAOImpl();
    @Override
    public void transfer(Account from, Account to) throws SQLException {
        Connection connection = null;
        try{
            connection = DBConnection.getConnection();
            connection.setAutoCommit(false);
            accountDAO.decrease(from);
            int i = 5 / 0;
            accountDAO.increase(to);
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
            if (connection != null){
                try {
                    connection.rollback();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }finally {
            DBConnection.closeAll();
        }
    }
}

你可能感兴趣的:(Java程序设计,数据库,java,mysql)