文章目录
为什么要学jdbc
1.jdbc是java和数据库的必要纽带。
2.一些应用层的框架底层代码是jdbc。
jdbc是什么
JDBC:Java Database Connectivity | java连接数据库技术
在java代码中可以使用jdbc提供的方法,可以发送字符串类型的SQL语句到数据库管理软件(MySQL,Oracle等)并获取语句执行的结果,进而实现数据库数据CRUD操作的技术。
jdbc是java连接数据库技术的统称,jdbc技术是一种典型的面向接口编程。
jdbc如何连接java程序和数据库软件
jdbc的组成
java语言提供规范(接口),规定数据库发操作方法,位于java.sql.javax.sql包下。
第三方数据库(实现类):各个数据库厂商,根据java的jdbc接口,完成具体的实现代码,提供jar包(类)。
注意:实现接口的代码可能不同,但是方法必然相同。
jdbc基本步骤
1.注册驱动,依赖jar包,进行安装。
2.建立连接
3.java程序创建sql语句对象
4.statement对象,发送sql语句到数据库,并获取返回结果result对象
5.解析结果集,即拆对象。
6.销毁资源,关闭connection,释放statement,释放result。
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
public class StatementQuery {
public static void main(String[] args) throws SQLException {
//1.注册驱动
//依赖:注意mysql8+以上版本,导入com.mysql.cj.jdbc.Driver
// 5+ 导入com.mysql.jdbc.Driver
DriverManager.registerDriver(new Driver());
//2.获取连接
//java程序连接数据库,需要调用某种方法,方法需要调用连入数据库的基本信息:
//数据库ip地址:127.0.0.1
//数据库端口号:3306
//账号
//密码
//连接数据库的名称
/*
参数1:url
语法:jdbc:数据库厂商名://ip地址:数据库端口号/数据库名
jdbc:mysql://127.0.0.1:3306/test
参数2:username 数据库软件的账号
参数3:password 数据库软件的密码
*/
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
//3.创建statement
Statement statement = connection.createStatement();
//4.发送sql语句并获取结果
String sql = "select * from t_user;";
ResultSet resultSet = statement.executeQuery(sql);
//5.结果集解析
//先看看有没有下一行数据
while(resultSet.next()){
int id = resultSet.getInt("id");
String account = resultSet.getString("account");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
System.out.println(id + "--" +account+ "--" + password + "--" + "nickname");
}
//6.关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
静态SQL路线(没有动态值语句 - statement)
基于statement实现模拟用户登入
缺点:1.SQL语句的拼接麻烦。
2.只能拼接字符串类型,其他数据库无法处理。
3.可能发生注入攻击:动态值充当sql语句影响原有的查询条件。
package com.atguigu.api.statement;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
/*
模拟用户登入
输入账号,密码
进行数据库查询
反馈成功或失败
*/
public class StatementUserLogIn {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//获取用户输入信息
Scanner scan = new Scanner(System.in);
System.out.println("请输入账号");
String account = scan.nextLine();
System.out.println("请输入密码");
String password = scan.nextLine();
//注册驱动
/*
问题:注册俩次驱动(在Driver类的对象创建时,会自动调用类的静态方法,这个静态方法中会注册一次)
即DriverManager.registerDriver(new Driver())本身就会注册一次。
static静态代码块也会注册一次。
解决:只想注册一次
只触发静态代码块即可。
*/
Class.forName("com.mysql.cj.jdbc.Driver");//要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
//获取连接
/*
getConnection是一个重载函数
允许开发者,用不同的形式传入数据库的参数
核心属性:
1.数据库软件所在的主机ip地址:127.0.0.1 | localhost
2.数据库软件所在的主机的端口号:3306
3.连接的具体库:test
4.连接账号:root
5.连接的密码:gpy
三个参数:(一般选1和3)
String url:数据库软件所在的信息,连接的具体库,以及其他的可选信息
格式:jdbc:数据库软件名称(mysql)://ip地址|主机名:端口号/数据库名?key=value&key=value...
jbdc:mysql://127.0.0.1:3306/test
jbdc:mysql://localhost:3306/test
本机的省略写法:如果数据的软件安装到本机可以进行一些省略,省略本机地址和默认端口号。
jdbc:mysql:///test
String username:root
String password:gpy
俩个参数:
String url:url与三个参数相同
Properties info:存储账号和密码
Properties类似于Map key(user) = value(password) 只不过都是字符串形式
一个参数:
数据库软件所在的信息,连接的具体库,以及其他的可选信息
格式:jdbc:数据库软件名称(mysql)://ip地址|主机名:端口号/数据库名?key=value&key=value...
jdbc:mysql:localhost:3306/test?user=root&password=gpy
user和password就算账号和密码,这是规定
url可选信息:
url?user=账号&password=密码
serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
serverTimezone=Asia/Shanghai:时区
useUnicode=true:是否使用Unicode编码格式
useSSL=true:是否忽略ssl的格式验证
驱动在8之后,不用写了
*/
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
//俩个参数
Properties info = new Properties();
info.put("user","root");
info.put("password","gpy678615");
Connection connection2 = DriverManager.getConnection("jdbc:mysql:///test", info);
//三个参数
Connection connection3 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?user=root&password=gpy678615");
//创建发送sql语句的statement对象
String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";
Statement statement = connection.createStatement();
/*
SQL分类
DDL:容器(库/表)的创建,修改,删除
DML:插入,修改,删除
DQL:查询语句
DCL:权限控制
TPL:事务控制语句
int row = statement.executeUpdate(sql) ---->非DQL
情况1:DML 返回影响的行数 eg删除3条return3 修改0条return0
情况2:非DML语句,return 0;
ResultSet resultSet = statement.executeQuery(sql); ---->DQL
resultSet:结果封装对象
*/
//int i = statement.executeUpdate(sql);
ResultSet resultSet = statement.executeQuery(sql);
//结果集解析
/*
想要进行数据解析,需要进行俩件事情:
1.移动游标,指向获取数据行
resultSet内部包含一个游标指向当前行数据
默认指向第0行,第一行之前
我们可以使用next方法移动游标,返回只是一个boolean类型
可以使用while loop获得每一行的数据
2.获取指定行的列数据
resultSet.get类型(String columnLabel | int columnIndex)
columnLabel:列名 | 别名
columnIndex:根据列的下角标从左到右,从1到结束
*/
// while(resultSet.next()){
// int id = resultSet.getInt(1);
// String account1 = resultSet.getString("account");
// String password1 = resultSet.getString(3);
// String nickname = resultSet.getString("nickname");
// System.out.println(id + " " + account + " " + password1 + " " + nickname);
// }
// 移动一次即成功
if(resultSet.next()){
System.out.println("成功");
}else{
System.out.println("失败");
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
scan.close();
}
}
动态SQL路线(动态值语句 - PreparedStatement)
package preparedstatement;
import java.sql.*;
import java.util.Scanner;
/*
使用预编译的statement完成用户登入
防止注入攻击 | 演示ps的使用流程
*/
public class PSUserLogIn {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.获取用户输入信息
Scanner scan = new Scanner(System.in);
System.out.println("请输入账号");
String account = scan.nextLine();
System.out.println("请输入密码");
String password = scan.nextLine();
//ps的数据库的流程
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
/*3.创建对象发送sql语句
preparedStatement
1.编写SQL语句结构,不包含动态值的语句。动态值部分使用?替代 ?只能替代动态值。
2.创建preparedStatement,并传入动态值。
3.占位符 ? 赋值
4.发送sql语句并获取结果。
*/
String sql = "select * from t_user where account = ? and password = ? ;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//参数1:占位符的位置,从左向右,容1开始 account:1 password:2
//参数2:占位符的值 可以设置任意类型的数据,拼接和类型更加丰富
preparedStatement.setObject(1,account);
preparedStatement.setObject(2,password);
//发送sql语句,并获取返回结果
//因为他已经知道语句,知道动态值,就不需要填入sql了
ResultSet resultSet = preparedStatement.executeQuery();
//5.结果集解析
if(resultSet.next()){
System.out.println("操作成功");
}else {
System.out.println("操作失败");
}
//6.关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
基于PreparedStatement进行CRUD
package preparedstatement;
import org.junit.Test;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
使用PreparedStatement对t_user进行CRUD操作
*/
public class PSCURD {
@Test
public void testInsert() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,"test");
preparedStatement.setObject(2,"test");
preparedStatement.setObject(3,"xzj");
int i = preparedStatement.executeUpdate();
if(i > 0){
System.out.println("插入成功");
}else{
System.out.println("数据插入失败");
}
preparedStatement.close();
connection.close();
}
@Test
public void testUpdate() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "update t_user set nickname = ? where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,"gpy");
preparedStatement.setObject(2,3);
int i = preparedStatement.executeUpdate();
if(i > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
preparedStatement.close();
connection.close();
}
@Test
public void testDelete() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "delete from t_user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,3);
int i = preparedStatement.executeUpdate();
if(i > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
preparedStatement.close();
connection.close();
}
/*
目标:查询所有数据,并封装到List<Map> list集合中
*/
@Test
public void testSelect() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "select id,account,password,nickname from t_user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
//ResultSetMetaData 对象装的就算当前结果集列的信息
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
List<Map> list = new ArrayList<Map>();
while(resultSet.next()){
Map map = new HashMap<>();
//纯手动
// map.put("id",resultSet.getObject(1));
// map.put("account",resultSet.getObject(2));
// map.put("password",resultSet.getObject(3));
// map.put("nickname",resultSet.getObject(5));
//自动遍历列,注意从1开始
for (int i = 1; i <= columnCount; i++) {
//获取指定类的下角标的值
Object value = resultSet.getObject(i);
//获取指定列的列名
//getColumnLabel获取别名和名称
String columnLabel = metaData.getColumnLabel(i);
map.put(columnLabel,value);
}
list.add(map);
}
System.out.println("list = " + list);
resultSet.close();
preparedStatement.close();
connection.close();
}
}
扩展提升1
主键回显:获取主键
批量插入数据优化
package preparedstatement;
import org.junit.Test;
import java.sql.*;
/*
PS的特殊使用情况
*/
public class PSOtherPart {
//主键回显
/*
t_user插入一条数据,并获取数据库自增长的主键
26行:创建PreparedStatement对象的时候,告知,携带会数据库增长的主键(sql,Statement.RETURN_GENERATED_KEYS);
36行:获取主键的结果集对象,一行 一列 id = 值
*/
@Test
public void returnPrimaryKey() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
//
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setObject(1,"test1");
preparedStatement.setObject(2,"12345");
preparedStatement.setObject(3,"wxy");
int i = preparedStatement.executeUpdate();
if(i > 0){
System.out.println("插入成功");
//取主键值
//获取主键的结果集对象,一行 一列 id = 值
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
generatedKeys.next();
int anInt = generatedKeys.getInt(1);
System.out.println(anInt);
}else{
System.out.println("插入失败");
}
preparedStatement.close();
connection.close();
}
/*
使用普通的方式插入1000条信息
*/
@Test
public void testInsert() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "gpy678615");
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
//
PreparedStatement preparedStatement = connection.prepareStatement(sql);
long l = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
preparedStatement.setObject(1, "test" + i);
preparedStatement.setObject(2, "12345" + i);
preparedStatement.setObject(3, "wxy" + i);
}
long l1 = System.currentTimeMillis();
System.out.println(l1-l);
int i = preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
}
/*
优化方式插入1000条信息
1.路径后添加:?rewriteBatchedStatements=true表示允许批量插入
2.insert into values 语句不能添加分号;
3.不是执行语句每条,是批量添加addBatch(),添加到values后面
4。同意批量执行
*/
@Test
public void testBatchInsert() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
//路径后加一个值:?rewriteBatchedStatements=true表示允许批量操作
Connection connection = DriverManager.getConnection("jdbc:mysql:///test?rewriteBatchedStatements=true", "root", "gpy678615");
String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
//
PreparedStatement preparedStatement = connection.prepareStatement(sql);
long l = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
preparedStatement.setObject(1, "test" + i);
preparedStatement.setObject(2, "12345" + i);
preparedStatement.setObject(3, "wxy" + i);
//不执行,追加到values后面
preparedStatement.addBatch();
}
//批量执行,批量操作
preparedStatement.executeBatch();
long l1 = System.currentTimeMillis();
System.out.println(l1-l);
preparedStatement.close();
connection.close();
}
}
扩展提升2
事务:银行卡业务方法
package com.atguigu.api.transation;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @program: jdbc
* @description: 银行卡业务方法,调用dao方法
* @author: Mr.Guan
* @create: 2023-02-05 18:27
*
**/
public class BankService {
public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
BankDao bankDao = new BankDao();
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
try {
//开启事务
//关闭MySQL的自动提交事务
connection.setAutoCommit(false);
//执行数据库动作
bankDao.add(addAccount, money,connection);
System.out.println("------------------");
bankDao.sub(subAccount, money,connection);
//提交事务
connection.commit();
}catch (Exception e){
//事务回滚
connection.rollback();
//抛出异常信息
throw e;
}finally {
connection.close();
}
}
/**
* TODO:
* 1.事务添加是在业务方法中
* 2.利用try catch代码块开启事务、提交事务和事务回滚
* 3.将connetion传入到dao层即可。dao只负责使用,不需要close
* @throws SQLException
* @throws ClassNotFoundException
*/
@Test
public void transferTest() throws SQLException, ClassNotFoundException {
transfer("lvdandan","ergouzi",500);
}
}
package com.atguigu.api.transation;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*
bank表的数据库方法存储类
*/
public class BankDao {
/**
* 加钱的数据库操作方法
* @param account 加钱的账号
* @param money 加钱的金额
*/
public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "update t_bank set money = money +? where account =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,account);
preparedStatement.executeUpdate();
preparedStatement.close();
System.out.println("加钱成功");
}
/**
* 减钱的数据库操作方法
* @param account 减钱的账号
* @param money 减钱的金额
*/
public void sub(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
String sql = "update t_bank set money = money -? where account =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,account);
preparedStatement.executeUpdate();
preparedStatement.close();
System.out.println("减钱成功");
}
}
Druid连接池
- 我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始,
我们可以先替用户先创建好一些连接对象,等用户要拿连接对象时,就直接从池中拿,
不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。 - 可以提高连接的使用率。当池中的现有的连接都用完了,那么连接池可以向服务器申
请新的连接放到池中。 - 直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。
Druid的使用
硬编码(不推荐)
/**
* 创建druid连接池对象,使用硬编码进行核心参数设置!
* 必须参数: 账号
* 密码
* url
* driverClass
* 非必须参数:
* 初始化个数
* 最大数量等等 不推荐设置
*/
@Test
public void druidHard() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
//设置四个必须参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql:///day01");
//获取连接
Connection connection = dataSource.getConnection();
// JDBC的步骤
//回收连接
connection.close();
}
软编码
public class DruidTest {
/**
* 通过读取外部配置文件的方法,实例化druid的连接池对象
*/
@Test
public void testSoft() throws Exception {
//1.读取外部配置文件
Properties properties = new Properties();
//2.src下的文件可以使用类加载器提供的方法
InputStream resourceAsStream = DruidTest.class.getClassLoader().getResourceAsStream("durid.properties");
properties.load(resourceAsStream);
//3.使用连接池工具类的工程模式,创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//4.获取连接
Connection connection = dataSource.getConnection();
//之后进行数据库的crud
connection.close();
}
}
工具类的封装
1.0版本
封装一个工具类,内部包含连接池对象,同时对外部提供连接的方法和回收方法。
缺点:无法同一个线程不同的方法得到同一个连接
package com.atguigu.api.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.sun.jdi.connect.spi.Connection;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
/**
* 内部包含连接池,并且对外提供获取连接和回收连接的方法
* 建议:工具类的方法,推荐写成静态
* @program: jdbc
* @description:
* @author: Mr.Guan
* @create: 2023-02-06 16:39
*
**/
public class JDBCUtils {
private static DataSource dataSource = null;//连接池对象
static {
Properties properties = new Properties();
InputStream resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("durid.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供连接的方法
public static Connection getConnection() throws SQLException {
return (Connection) dataSource.getConnection();
}
//回收连接的方法
//连接池的连接调用close是回收
public static void freeConnection(Connection connection) throws IOException {
connection.close();
}
}
package com.atguigu.api.utils;
import com.sun.jdi.connect.spi.Connection;
import java.io.IOException;
import java.sql.SQLException;
/**
* @program: jdbc
* @description:
* @author: Mr.Guan
* @create: 2023-02-06 16:52
**/
public class JDBCUtilCRUD {
public static void main(String[] args) throws SQLException, IOException {
Connection connection = JDBCUtils.getConnection();
JDBCUtils.freeConnection(connection);
}
}
2.0版本
考虑事务的情况下。同一个线程不同的方法获得同一个连接
package com.atguigu.api.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @program: jdbc
* @description:
* @author: Mr.Guan
* @create: 2023-02-06 16:39
*TODO:
* 利用线程本地变量,存储连接信息!确保一个线程的多个方法获取同一个连接。
* 优势:事务操作的时候,service和dao方法属于同一个线程,不用再传递参数了。
* 大家都可以调用getConnection,获取相同的连接。
**/
public class JDBCUtilsV2 {
private static DataSource dataSource = null;//连接池对象
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
static {
Properties properties = new Properties();
InputStream resourceAsStream = JDBCUtilsV2.class.getClassLoader().getResourceAsStream("durid.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供连接的方法
//先查看线程本地变量是否存在
public static Connection getConnection() throws SQLException {
Connection connection = tl.get();
if (connection == null) {
//线程本地变量没有,连接池获取
connection = dataSource.getConnection();
tl.set(connection);
}
return connection;
}
//回收连接的方法
//连接池的连接调用close是回收
public static void freeConnection() throws IOException, SQLException {
Connection connection = tl.get();
if (connection == null) {
tl.remove();//清空线程本地数据
connection.setAutoCommit(true);//事务回归
connection.close();//回收到连接池
}
}
}
package com.atguigu.api.transation;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*
bank表的数据库方法存储类
*/
public class BankDao {
/**
* 加钱的数据库操作方法
* @param account 加钱的账号
* @param money 加钱的金额
*/
public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "update t_bank set money = money +? where account =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,account);
preparedStatement.executeUpdate();
preparedStatement.close();
System.out.println("加钱成功");
}
/**
* 减钱的数据库操作方法
* @param account 减钱的账号
* @param money 减钱的金额
*/
public void sub(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
String sql = "update t_bank set money = money -? where account =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,account);
preparedStatement.executeUpdate();
preparedStatement.close();
System.out.println("减钱成功");
}
}
package com.atguigu.api.transation;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @program: jdbc
* @description: 银行卡业务方法,调用dao方法
* @author: Mr.Guan
* @create: 2023-02-05 18:27
*
**/
public class BankService {
public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
BankDao bankDao = new BankDao();
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "gpy678615");
try {
//开启事务
//关闭MySQL的自动提交事务
connection.setAutoCommit(false);
//执行数据库动作
bankDao.add(addAccount, money,connection);
System.out.println("------------------");
bankDao.sub(subAccount, money,connection);
//提交事务
connection.commit();
}catch (Exception e){
//事务回滚
connection.rollback();
//抛出异常信息
throw e;
}finally {
connection.close();
}
}
/**
* TODO:
* 1.事务添加是在业务方法中
* 2.利用try catch代码块开启事务、提交事务和事务回滚
* 3.将connetion传入到dao层即可。dao只负责使用,不需要close
* @throws SQLException
* @throws ClassNotFoundException
*/
@Test
public void transferTest() throws SQLException, ClassNotFoundException {
transfer("lvdandan","ergouzi",500);
}
}
BaseDao
简化非DQL语句
public int executeUpdate(String sql,Object... params) throws SQLException, IOException {
Connection connection = JDBCUtilsV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 1; i <= params.length; i++) {
preparedStatement.setObject(i, params[i]);
}
int rows = preparedStatement.executeUpdate();
preparedStatement.close();
//是否回收连接,需要考虑是不是事务
if (connection.getAutoCommit()) {
//为真则没有开启事务
JDBCUtilsV2.freeConnection();
}
return rows;
}
public void testInsert() throws ClassNotFoundException, SQLException, IOException {
String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
int i = executeUpdate(sql,"测试333", "333", "ergoiuzi");
}
简化DQL语句
/**
* 非DQL语句封装
* 数据库数据--》java的实体类
*
*/
public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
Connection connection = JDBCUtilsV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if(params != null && params.length != 0){
for (int i = 1; i <= params.length; i++) {
preparedStatement.setObject(i, params[i-1]);
}
}
ResultSet resultSet = preparedStatement.executeQuery();
List<T> list = new ArrayList<>();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while(resultSet.next()){
T t = clazz.newInstance();//调用类的无参构造函数
//自动遍历列,注意从1开始
for (int i = 1; i <= columnCount; i++) {
//获取指定类的下角标的值
Object value = resultSet.getObject(i);
//获取指定列的列名
//getColumnLabel获取别名和名称
String columnLabel = metaData.getColumnLabel(i);
//反射给对象的属性值
Field filed = clazz.getDeclaredField(columnLabel);
filed.setAccessible(true);//属性可以设置,打破私有的修饰
filed.set(t, value);
}
list.add(t);
}
resultSet.close();
preparedStatement.close();
if(connection.getAutoCommit()){
JDBCUtilsV2.freeConnection();
}
return list;
}