文章目录
JDBC
JDBC是Java提供的一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商需要针对这套接口提供不同的实现。
快速入门
需要先引入驱动jar包mysql-connector-java
public static void main(String[] args) throws Exception {
Driver driver = new Driver(); // 获取驱动
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
// user=root password=xxx
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
Connection connect = driver.connect(url, properties); // 获取连接
String sql = "insert into tb_user(username,address) values('lxg','tj');";
Statement statement = connect.createStatement(); //拿到可以执行sql语句的句柄
int i = statement.executeUpdate(sql); // 返回影响的行数
System.out.println(i); // 1
//关闭连接资源
statement.close();
connect.close();
}
获取数据库连接 4 种方式
方式一:获取Driver实现类对象
Driver driver = new Driver();
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
Connection connect = driver.connect(url, properties);
System.out.println(connect);
connect.close();
方式二:使用反射动态加载
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
Connection connect = driver.connect(url, properties);
System.out.println(connect);
connect.close();
方式三:使用DriverManager替换Driver
DriverManager可以注册多个驱动,并且会自动选择合适的驱动来创建连接。在需要支持多种数据库时,使用DriverManager可以让代码更加灵活,更易于管理和维护。
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(url, properties);
System.out.println(connection);
connection.close();
方式四:使用DriverManager自动完成注册
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql:///user";
String user = "root";
String password = "xxx";
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println(connection);
connection.close();
ResultSet结果集
ResultSet表示数据表的结果集,通过select查询语句生成。
ResultSet对象保存一个光标指向其当前的数据行。最初,光标位于第一行之前。
next方法将光标移动到下一行,若下一行没有数据则返回false,因此可以在while循环中遍历结果集
代码示例
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(url, properties);
Statement statement = connection.createStatement();
String sql = "select * from tb_user";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String address = resultSet.getString("address");
System.out.println(id + " " + name + " " +address);
}
statement.close();
connection.close();
Statement和PreparedStatement
Statement对象用于执行静态SQL语句并返回其生成的结果的对象。
但Statement执行SQL语句时存在SQL注入的风险。
PreparedStatement采用预处理的机制来解决SQL注入的问题。
PreparedStatement执行的SQL语句中的参数用(?)来表示,调用PreparedStatement对象的setXxx()方法来设置这些参数。
预处理机制
当我们使用PreparedStatement对象执行SQL语句时,该SQL语句会被发送到数据库服务器,数据库服务器会对SQL语句进行解析,确定SQL语句的语义,然后编译SQL语句,并生成执行计划。这个过程只在第一次执行该SQL语句时进行,生成的执行计划会被缓存起来,以后再执行相同的SQL语句,就直接使用缓存的执行计划,不需要再次解析和编译,从而提高了执行效率。
预处理的优势:
- 不再使用 + 拼接sql语句,减少语法错误。
- 有效的解决了sql注入问题。
- 大大减少了编译次数,效率较高。
PreparedStatement代码
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql:///user";
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/java/com/lhs/mysql.properties"));
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(url, properties);
String sql = "select * from tb_user where id = ? and username = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,109);
preparedStatement.setString(2,"lhs123");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String address = resultSet.getString("address");
System.out.println(id + " " + name + " " +address);
}
preparedStatement.close();
connection.close();
封装 JDBCUtils
在jdbc操作中,获取连接和释放资源是经常用到的,可以将其封装成工具类。
public class JDBCUtils {
private static Properties properties;
private static String url;
static {
try {
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
url = "jdbc:mysql:///user";
properties = new Properties();
properties.load(new FileInputStream("src\\main\\java\\com\\lhs\\mysql.properties"));
DriverManager.registerDriver(driver);
} catch (Exception e) {
//在实际开发中,我们可以这样处理
//1. 将编译异常转成 运行异常
//2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便.
throw new RuntimeException(e);
}
}
//连接数据库, 返回 Connection
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,properties);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//关闭相关资源
/*
1. ResultSet 结果集
2. Statement 或者 PreparedStatement
3. Connection
4. 如果需要关闭资源,就传入对象,否则传入 null
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection){
try {
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
测试:
Connection connection = JDBCUtils.getConnection();
String sql = "select * from tb_user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String address = resultSet.getString("address");
System.out.println(id + " " + name + " " + address);
}
JDBCUtils.close(resultSet,preparedStatement,connection);
事务
JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务的。
调用Connection的**setAutoCommit(false)**可以取消自动提交事务。
在所有SQL语句都成功执行后,调用Connection的**commit()**方法提交事务。
在其中某个操作失败或出现异常时,调用Connection的**rollback()**方法回滚事务。
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
//取消自动提交事务
connection.setAutoCommit(false);
String sql1 = "update tb_user set address = 'bj' where id = 1";
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.execute();
String sql2 = "update tb_user set address = 'sh' where id = 2";
preparedStatement.execute(sql2);
//提交事务
connection.commit();
}catch (Exception e){
//若执行失败则回滚
connection.rollback();
System.out.println(e);
}finally {
JDBCUtils.close(null,preparedStatement,connection);
}
批量处理
当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下效率会比单独提交高。
JDBC连接MySQL时,如果要使用批量处理功能,需要在url中添加?rewriteBatchedStatements=true
JDBC的批量处理语句包括下面方法:
1)addbatch():添加需要批量处理的SQL语句或参数。
2)executeBatch():执行批量处理语句。
3)clearBatch():清空批量处理包的语句。
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into tb_user(username,address) values(?,?)";
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < 10000; i++) {
preparedStatement.setString(1,"lxg" + i);
preparedStatement.setString(2,"tj");
preparedStatement.addBatch();
// 每添加2000条语句执行一次
if(i % 2000 == 0){
preparedStatement.executeBatch();
preparedStatement.clearBatch();
}
}
}catch (Exception e){
System.out.println(e);
}finally {
JDBCUtils.close(null,preparedStatement,connection);
}
数据库连接池
数据库连接池用于管理数据库连接,以提高数据库访问的性能和效率。连接池会在应用程序启动时创建一定数量的数据库连接,并将这些连接保存在池中。当应用程序需要访问数据库时,它会从连接池中获取一个可用的连接,使用完毕后再将连接放回连接池,而不是每次都重新创建和销毁连接。
C3P0
C3P0数据库连接池速度相对较慢,稳定性不错,是spring框架默认使用的连接池。
连接方式一
//1. 创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2. 通过配置文件 mysql.properties 获取相关连接的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\main\\java\\com\\lhs\\mysql.properties"));
//读取相关的属性值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//给数据源 comboPooledDataSource 设置相关的参数
//注意:连接管理是由 comboPooledDataSource 来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
//最大连接数
comboPooledDataSource.setMaxPoolSize(50);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis(); for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection(); //这个方法就是从 DataSource 接口实现的
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
方式二:使用配置文件
创建c3p0-config.xml文件
<c3p0-config>
<named-config name="hello">
<!-- 驱动类 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- url-->
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/user</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">xxx</property>
<!-- 每次增长的连接数-->
<property name="acquireIncrement">5</property>
<!-- 初始的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>
<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("c3p0-config.xml");
long start = System.currentTimeMillis(); for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection(); //这个方法就是从 DataSource 接口实现的
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
Druid(德鲁伊)
导入相关jar包并加入配置文件到src目录下
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/user?rewriteBatchedStatements=true
username=root
password=xxx
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)
maxWait=5000
Properties properties = new Properties();
properties.load(new FileInputStream("src\\main\\java\\com\\lhs\\druid.properties"));
//4. 创建一个指定参数的数据库连接池, Druid 连接池
DataSource dataSource =
DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis(); for (int i = 0; i < 5000; i++) {
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid 5000 连接 mysql 耗时=" + (end - start));
Apache—DBUtils
使用select查询语句可得到resultSet,但关闭连接后就无法使用resultSet,并且resultSet不利于数据管理。
DBUtils能将查询的结果保存到映射类中并封装成集合返回。
1)要使用Apache—DBUtil,首先需要在项目中引入相关的依赖。
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.8.1</version>
</dependency>
2)创建与表数据相映射的类
public class User implements Serializable {
private int id;
private String username;
private String address;
public User() {
}
public User(int id, String username, String address) {
this.id = id;
this.username = username;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String name) {
this.username = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
3)使用DBUtils查询数据
Connection connection = JDBCUtils.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select * from tb_user where id < 100";
// 若查询单条数据可使用BeanHandler,返回单个对象。
List<User> userlist = queryRunner.query(connection,sql, new BeanListHandler<>(User.class));
for (User user : userlist) {
System.out.println(user);
}
JDBCUtils.close(null,null,connection);
DAO
DAO(Data Access Object)是一种设计模式,用于将应用程序的业务逻辑和数据访问逻辑分离。DAO 主要负责封装对数据的访问和操作,隐藏了底层数据库操作的细节,使业务逻辑与数据访问逻辑解耦,提高了代码的可维护性和可测试性。
我们可以将增删改查的方法封装到dao中,使用时直接调用dao方法传入参数即可。
public class UserDao {
private Connection connection;
public UserDao(Connection connection) {
this.connection = connection;
}
public List<User> getAllUsers() {
List<User> userList = new ArrayList<>();
String query = "SELECT * FROM users";
try (PreparedStatement statement = connection.prepareStatement(query);
ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
User user = new User(id, name, age);
userList.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return userList;
}
}