JDBC就是使用java语言操作关系型数据库的一套API,全称java数据库连接。
@Test
public void jdbcTest() throws ClassNotFoundException, SQLException {
//获取驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//建立连接
String url = "jdbc:mysql://localhost:3306/test";
Connection connection = DriverManager.getConnection(url, "root", "root");
//编写SQL语句
String sql = "select * from student";
//获取执行SQL的对象
Statement statement = connection.createStatement();
//执行SQL
ResultSet resultSet = statement.executeQuery(sql);
//处理返回结果
while(resultSet.next()){
System.out.print(resultSet.getInt("id") + "\t");
System.out.print(resultSet.getString("name") + "\t");
System.out.println(resultSet.getString("num"));
System.out.println("-------------------------");
}
//释放资源
resultSet.close();
statement.close();
connection.close();
}
在上面我们发现,我们注册驱动的时候用的是这行代码
Class.forName("com.mysql.cj.jdbc.Driver");//加载Driver类
我们来看一下Driver的源码,在源码中用一个静态代码块
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
那么我们是不是可以这样来注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
我们可以参考 简单了解java代码块,发现静态代码块随着类的加载而加载,上面的写法程序可以运行成功,但是没有必要,因为执行以上代码后,相当于new了两次Driver。我们只需要加载一次Driver类静态代码块就会自动执行,成功注册驱动。
补充:MySQL 5之后的驱动包,可以省略注册驱动的步骤
| 类 | 方法 |
|---|---|
| static Connection | getConnection(String url, String name, String password) |
参数说明:
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306/test
细节:
普通执行SQL
Statement createStatement()
预编译SQL的执行SQL对象:防止SQL注入
PrepareStatement prepareStatement(sql)
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。简单来说就是用户在界面提交数据时,人为的添加一些特殊字符,使得sql语句结构发生了变化,最终在没有用户或者密码的情况下进行登录。
PreparedStatement是Statement的子接口,可以防止SQL注入问题。可以根据Connection接口中的prepareStatement(sql)方法获得PreparedStatement对象。
注意:sql提前创建好的,sql语句中需要参数。使用?进行占位,比如:
select * from user where username = ? and password = ?;
步骤一:
PreparedStatement pstmt = conn.prepareStatement(sql);
步骤二:设置参数(执行sql之前):pstmt.setXxx(int index, 要放入的值),根据不同类型的数据进行方法选择。
参数说明:
第一个参数:int index表示的是问号出现的位置,问号是从1开始计数的
第二个参数:给问号的位置传入值
/**
* PreparedStatement
*/
public class JDBCDemo1 {
@Test
public void jdbcTest() throws ClassNotFoundException, SQLException {
//获取驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
DriverManager.registerDriver(new Driver());
//建立连接
String url = "jdbc:mysql://localhost:3306/test";
Connection connection = DriverManager.getConnection(url, "root", "root");
//编写SQL语句
String sql = "select * from student where id = ? or name = ?";
//获取执行SQL的对象
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, 1);
pstmt.setString(2, "糖魅");
//执行SQL
ResultSet rs = pstmt.executeQuery();
//处理返回结果
while(rs.next()){
System.out.print(rs.getInt("id") + "\t");
System.out.print(rs.getString("name") + "\t");
System.out.println(rs.getString("num"));
System.out.println("-------------------------");
}
//释放资源
rs.close();
pstmt.close();
connection.close();
}
}
MySQL事务管理
开启事务:BEGIN; START TRANSACTION
提交事务:COMMIT;
回滚事务:ROLLBACK
MySQL默认自动提交事务
JDBC事务管理:Connection接口定义了三个对应的方法
开启事务:SetAutoCommit(boolean autoCommit):true为自动提交事务、false为手动提交事务,即开启事务
提交事务:commit()
回滚事务:rollback()
执行SQL语句,具体为一下两种:
int executeUpdate(sql):执行DML、DDL语句
返回值:
1、DML语句影响的行数
2、DDL语句执行后,执行成功返回0
ResultSet executeQuery(sql):执行DQL语句
返回值:ResultSet结果集对象
while(rs.next){
rs.getXxx(参数);
}
抽取其他jdbc代码中的重复代码
示例:
工具类:
public class JDBCUtil {
//注册驱动所需参数
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
//建立连接所需参数
private static final String URL = "jdbc:mysql://localhost:3306/test";
private static final String NAME = "root";
private static final String PASSWORD = "root";
//注册驱动
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
//建立连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, NAME, PASSWORD);
}
//释放资源
public static void close(Statement stmt, Connection conn) {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
运用工具类:
public class JDBCDemo1 {
@Test
public void jdbcTest() throws ClassNotFoundException, SQLException {
//注册驱动并获取连接
Connection connection = JDBCUtil.getConnection();
//编写SQL语句
String sql = "select * from student where id = ? or name = ?";
//获取执行SQL的对象
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, 1);
pstmt.setString(2, "糖魅");
//执行SQL
ResultSet rs = pstmt.executeQuery();
//处理返回结果
while (rs.next()) {
System.out.print(rs.getInt("id") + "\t");
System.out.print(rs.getString("name") + "\t");
System.out.println(rs.getString("num"));
System.out.println("-------------------------");
}
//释放资源
JDBCUtil.close(rs, pstmt, connection);
}
}
处理业务逻辑并调用Dao层的方法
和数据库交互(基层利用jdbc技术)
就是降低代码之间的依赖关系
那一层出问题,直接找那一层
那一层需要添加代码,直接添加即可
一个方法可以被其他层重复利用
模拟示例:
模拟web层
public class FindStudent {
@Test
public void test(){
//创建Student类
Student student = new Student();
String name = "糖解";
String num = "2021110614";
boolean judge = false;
//将数据进行封装
student.setName(name);
student.setNum(num);
//判断是否存在该学生
judge = StudentService.findStudent(student);
if(judge){
System.out.println("该学生存在~");
}else{
System.out.println("不存在该学生");
}
}
}
模拟service层
public class StudentService {
public static boolean findStudent(Student student){
Student stu = StudentDao.res(student);
if(stu != null){
return true;
}
return false;
}
}
模拟dao层
public class StudentDao {
public static Student res(Student student){
//System.out.println(student.getName() + " " + student.getName());
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Student stu = null;
try {
//创建数据库连接
conn = JDBCUtil.getConnection();
//编写sql
String sql = "select * from student where name = ? and num = ?";
//执行sql
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,student.getName());
pstmt.setString(2,student.getNum());
//处理结果
rs = pstmt.executeQuery();
if(rs != null && rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String num = rs.getString("num");
stu = new Student(id, name, num);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtil.close(rs, pstmt, conn);
}
return stu;
}
}
Connection getConnection();
创建数据库连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(properties对象);
我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。
Druid连接池的配置文件名字随意,放到src目录或者项目根目录下加载“druid.properties”文件内容(示例):
#配置数据库连接相关参数
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=false
username=root
password=root
#配置连接池相关参数
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
如何加载配置文件呢?请看下方:
//InputStream in = DruidDemo.class.getClassLoader().getResourceAsStream("druid,properties");
//Properties prop = new Properties();
//prop.load(in);
Properties prop = new Properties();
prop.load(new FileInputStream("druid.properties"));
从数据库连接池中,获取Connection对象
Connection conn = ds.getConnection();
接下来就是jdbc的常规操作了
public class DruidDemo {
@Test
public void druidTest() throws Exception {
//导入jar包
//加载properties文件
Properties prop = new Properties();
prop.load(new FileInputStream("druid.properties"));
//利用Druid工厂类,获取数据库连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//从连接池中获取一个Connection对象
Connection conn = dataSource.getConnection();
//编写sql语句
String sql = "select * from student";
//获取执行sql的对象
PreparedStatement pstm = conn.prepareStatement(sql);
//执行sql
ResultSet rs = pstm.executeQuery();
//处理结果
while(rs.next()){
System.out.print(rs.getInt("id") + " ");
System.out.print(rs.getString("name") + " ");
System.out.println(rs.getString("num"));
System.out.println("---------------------");
}
//释放资源
rs.close();
pstm.close();
//将连接归还连接池
conn.close();
}
}