先来看一个简单的demo
CREATE TABLE `users` (
`id` int NOT NULL,
`NAME` varchar(40) DEFAULT NULL,
`PASSWORD` varchar(40) DEFAULT NULL,
`email` varchar(60) DEFAULT NULL,
`birthday` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCFirstDemo {
public static void main(String[] args) throws Exception {
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//用户信息和url
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "root";
String sql = "SELECT * FROM users";
//连接数据库,记得释放资源
try (Connection connection = DriverManager.getConnection(url, username, password);
final Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
//resultset第一个是头指针,是没有数据的
while (resultSet.next()) {
System.out.println(resultSet.getObject("id"));
}
}
}
}
java连接数据库一共分为以下几步:
_
1.加载驱动
Class.forName(“com.mysql.cj.jdbc.Driver”);
_
2.连接数据库
Connection connection = DriverManager.getConnection(url, username, password);
_
3.与数据库进行交互
1)Statement
用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。 Statement接口不能接受参数。
2)PreparedStatement
当计划要多次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。可以防止sql注入,并且效率更高。
3)CallableStatement
当想要访问数据库存储过程时使用。CallableStatement接口也可以接受运行时输入参数。
_
4.获取查询结果
ResultSet resultSet = statement.execute(sql)
ResultSet resultSet = statement.executeQuery(sql);
ResultSet resultSet = statement.executeUpdate(sql);
_
5.释放资源
用try with resource或者手动释放
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
由于加载驱动、连接数据库还有释放资源的操作是固定的,所以我们可以把它封装到工具类中。
补充:
下面程序中获取配置文件的输入流我使用了三种方法
1.通过类加载器获取,限制是只能获取classpath下的文件(就是resources文件夹下的文件),不能用绝对路径来获取;
2.直接通过class来获取,能自动识别是相对路径还是绝对路径,path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取;
3.通过文件输入流来获取,这里是用的绝对路径。
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
//final InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
final InputStream inputStream = JDBCUtils.class.getResourceAsStream("db.properties");
//final InputStream inputStream = new FileInputStream(new File("/Users/riter/Documents/work/java/JDBC/src/main/resources/db.properties"));
final Properties properties = new Properties();
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//驱动只用加载一次
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放资源
public static void release(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
下面是配置文件
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = root
然后就可以写测试了
可以看到这里的connection,statement还有resultset的值都是工具类里配置的,实现了解耦,提高了可复用性。
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) throws Exception {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
connection = JDBCUtils.getConnection();
statement = connection.createStatement();
String sql = "INSERT INTO users ( id, `NAME`, `PASSWORD`, `email`, `birthday` )" +
"VALUES" +
"(" +
"4," +
"'hzh'," +
"'123456'," +
"'767641495@qq.com '," +
"'2020-11-09')";
final int i = statement.executeUpdate(sql);
if (i > 0) {
System.out.println("Success!");
}
JDBCUtils.release(connection, statement, resultSet);
}
}
这样去写的话,可能会产生sql注入,所以可以考虑将statement换成preparedStatement,它会将传入的参数当作字符串,将符号进行转义。mybatis底层就是用这个来实现的,需要重点理解。
- 预编sql语句
- 参数用?代替
- 再用方法手动注入参数
- 执行sql
- 释放资源
import java.sql.Connection;
import java.sql.PreparedStatement;
public class TestPreoaredStatement {
public static void main(String[] args) throws Exception {
Connection connection = JDBCUtils.getConnection();
//使用?占位符代替参数
String sql = "INSERT INTO users ( id, `NAME`, `PASSWORD`, `email`, `birthday` )" +
"VALUES" +
"(" +
"?," +
"?," +
"?," +
"?," +
"?)";
//预编译sql,先写sql,但不执行
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//手动设置参数
//给第一个参数赋值5
preparedStatement.setInt(1, 5);
//依此类推
preparedStatement.setString(2, "xiaoCai");
preparedStatement.setString(3, "767641495");
preparedStatement.setString(4, "767641495@qq.com");
preparedStatement.setDate(5, new java.sql.Date(new java.util.Date().getTime()));
//执行sql
final int i = preparedStatement.executeUpdate();
if (i > 0) {
System.out.println("Success");
}
JDBCUtils.release(connection, preparedStatement, null);
}
}
事务的实现
- 关闭自动提交(开启事务)
- 增删改完成后,提交
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(40) DEFAULT NULL,
`money` float DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
//关闭自动提交后,自动开启事务
connection.setAutoCommit(false);
String sql = "update account set money = money - 100 where name = 'A' ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
String sql1 = "update account set money = money + 100 where name = 'B' ";
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.executeUpdate();
//业务完成,提交事务
connection.commit();
System.out.println("Success!");
} catch (SQLException throwable) {
//失败就回滚
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
throwable.printStackTrace();
}
}
}
最后就是线程池部分
可以使用现成的线程池,也可以自己实现DataSource接口,自己写