JDBC详解

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林小果呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值