关于Spring连接数据库

版权声明:未经博主同意,谢绝转载!(请尊重原创,感谢) https://blog.csdn.net/topdeveloperr/article/details/85052710

目录

概述

java原生JDBC

JdbcTemplate

jdbcTemplate查询query

jdbcTemplate更新操作update/delete...

jdbcTemplate最佳实践

数据库连接和DataSource

总结


 

概述

Spring框架提供了可扩展的SQL数据库的支持。然而spring的东西使用久了之后,如果不去了解背后的原理的话可能越来越迷糊,本篇博客的目的是为了 分析spring框架如何实现的和各种数据库在各种场景下的连接配置的问题。我们将从最原生java数据库连接讲起。

java原生JDBC

相信不少学习java的朋友在第一次接触数据库的时候,一定都接触过最原生的数据库连接,以mysql为例,大致代码按如下:

try{
    //加载JDBC驱动程序:
    Class.forName("com.mysql.jdbc.Driver");

    String url = "jdbc:mysql://127.0.0.1:3306/dbname";
    String username = "";
    String password = "";

    // 创建与MySQL数据库的连接类的实例
    Connection conn = DriverManager.getConnection(url, username, password);

    // 用conn创建Statement对象类实例
    Statement sql_statement = conn.createStatement();

    // 执行sql
    ResultSet result = sql_statement.executeQuery(query);
    //处理结果
     ...
}catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                // 关闭连接
                try {
                    conn.close();
                    System.out.println("Database connection terminated");
                } catch (Exception e) { /* ignore close errors */
                }
      }
 }

上面的代码就是纯jdbc的连接数据库代码,只需安装jdk导入java 的原生包就可以写出来,不需要使用任何第三方的包。

上述代码主要有下面这几个步骤:

  1. 加载JDBC驱动程序
  2. 创建数据库的连接(需要用户名密码等信息) 
  3. 创建一个Statement  
  4. 执行SQL语句(处理结果)
  5. 处理异常,关闭连接  

上面的代码可以看出一些问题,比如模版代码过多,对于一个用户来说,他可能只是想执行一条sql语句,并不想关心其他连接,异常处理,加载驱动的问题。我们可以很容易想到的是,如果有一个方法或者一个工具,让用户可以只需要指定好数据库连接信息,就可以执行各种数据库操作,岂不是非常好吗?这就是框架开发这想做的事情。

JdbcTemplate

JdbcTemplate是spring提供的最经典也是最流行的jdbc实现方式之一。现在除了JdbcTemplate之外,还有SimpleJdbcInsert,SimpleJdbcCall两个更新的实现方式。

JdbcTemplate是“最底层”的方法,所有其他spring的方法在他们的包装之下都使用了JdbcTemplate。

JdbcTemplate是spring JDBC core这个包中的重点类。 它处理资源的创建和释放,帮助我们避免常见错误,例如上面直接使用java原生代码而忘记关闭连接。 它执行核心JDBC工作流的基本任务(例如语句创建和执行),应用程序代码只需要提供SQL并提取结果。 JdbcTemplate类主要包含下面这些功能:

  • 执行SQL查询语句
  • 执行更新语句和存储过程调用
  • 对ResultSet实例执行迭代并提取返回的参数值。
  • 捕获JDBC异常并将它们转换为org.springframework.dao packag中定义的通用的,信息量更大的异常层次结构。

使用jdbcTemplate需要配置datasource。使用spring ioc容器的话,则可以把datasource配置为一个bean,直接让数据库操作的时候引用即可。spring文档特别指出:应始终将DataSource配置为Spring IoC容器中的bean。这说明spring对于ioc容器中的datasource bean提供了非常多方便的功能。

我们可以使用jdbcTemplate来执行查询,更新,以及一些其他操作。如下:

jdbcTemplate查询query

下面的例子是返回数据的数量

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);

下面的查询是返回一个字符串:

String lastName = this.jdbcTemplate.queryForObject(
        "select last_name from t_actor where id = ?",
        new Object[]{1212L}, String.class);

也可以查询并填充单个对象:

Actor actor = this.jdbcTemplate.queryForObject(
        "select first_name, last_name from t_actor where id = ?",
        new Object[]{1212L},
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

查询并填充一系列对象:

List<Actor> actors = this.jdbcTemplate.query(
        "select first_name, last_name from t_actor",
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

在应用程序当中为了分离逻辑,我们常常将上述代码的RowMapper逻辑提出去,变成如下两个方法:

public List<Actor> findAllActors() {
    return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}

private static final class ActorMapper implements RowMapper<Actor> {

    public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
        Actor actor = new Actor();
        actor.setFirstName(rs.getString("first_name"));
        actor.setLastName(rs.getString("last_name"));
        return actor;
    }
}

值得一提的是,上述代码在spring batch的读数据环节也有应用。

jdbcTemplate更新操作update/delete...

下面是一个insert插入的例子:

this.jdbcTemplate.update(
        "insert into t_actor (first_name, last_name) values (?, ?)",
        "Leonor", "Watling");

下面是一个更新update的例子:

this.jdbcTemplate.update(
        "update t_actor set last_name = ? where id = ?",
        "Banjo", 5276L);

下面是一个删除的例子:

this.jdbcTemplate.update(
        "delete from actor where id = ?",
        Long.valueOf(actorId));

事实上上述三个操作我们可以看到调用的都是jdbcTemplate的同一个方法,只是sql语句不同。

jdbcTemplate最佳实践

使用jdbcTemplate的好处有线程安全,配置简单,只需一次配置即可等有点。在使用jdbcTemplate的时候也有最佳实践可以遵循。

使用JdbcTemplate类时的常见做法是在Spring配置文件中配置DataSource,然后将共享的DataSource的bean依赖注入到DAO类中。 JdbcTemplate是在DataSource的setter中创建的。 这意味着DAO类有似于以下的代码:

public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // JDBC-backed implementations of the methods on the CorporateEventDao follow...
}

对应的datasource的xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <context:property-placeholder location="jdbc.properties"/>

</beans>

在使用springboot开发的实际体验中,我们常常用java注解配置以及application.properties配置文件代替上述内容,但是其内容都是一样的,必定有驱动名,url,用户名,密码等信息,这就是和我们使用原生jdbc代码去连数据库有联系的地方。无论使用何种方式,该有的一个也不会少。

无论您选择使用上述哪种模板初始化样式,每次要运行SQL时,很少有需要创建JdbcTemplate类的新实例。 配置完成后,JdbcTemplate实例是线程安全的。 如果我们应用程序访问多个数据库,则可能需要多个JdbcTemplate实例(例如做数据迁移,就是一个典型的场景),这需要多个DataSource,随后需要多个不同配置的JdbcTemplate实例。

关于JdbcTemplate的内容我们暂时了解到这里,他当然还提供了很多其他方法,可以在需要使用时直接查看其文档或者接口即可。

数据库连接和DataSource

我们在上面已极多次提到了DataSource,可以猜出它是spring用来管理数据库连接的,现在,我们专门来研究一下dataSource的内容。

Spring通过DataSource获取与数据库的连接。 DataSource是java定义的JDBC规范的一部分,是一个通用的连接工厂。 它允许容器或框架从应用程序代码中隐藏连接池和事务管理问题。在大型项目中,作为开发人员,则无需了解有关如何连接到数据库的详细信息。 这是设置数据源的管理员的责任。 

使用Spring的JDBC层时,可以从JNDI获取数据源,也可以使用第三方提供的连接池实现配置自己的数据源。 流行的实现是Apache Jakarta Commons DBCP和C3P0。 Spring发行版中的实现仅用于测试目的,不提供池。

以下示例是如何在Java中配置DriverManagerDataSource类型的datasource的例子:

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");

可以看到配置的就是连接数据库必须的信息。

下面是一个DBCP实现的DataSource配置

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

虽然实现类不一样,其他其要求的配置都是一样的。

除了上述的配置方式外,我们还有下面这些方法配置datasource

  • 使用DataSourceUtils类提供的功能,
  • 实现SmartDataSource
  • 继承AbstractDataSource
  • 使用SingleConnectionDataSource
  • 使用DriverManagerDataSource
  • 使用TransactionAwareDataSourceProxy

上述方式就不一一展开,我们可以在使用时根据需求选择。

总结

最后做个总结,我们不难看出,无论使用spring提供的什么方式去进行数据库操作,我们始终需要去配置datasouce,而无论使用哪种实现的datasource我们也必须提供和配置对应的和原生jdbc连接一样的数据库连接信息,springboot通过jar包可以帮我们决定驱动,但是url,用户名,密码仍然要我们提供。我们不难想到其实现必然要做原生jdbc连接做的事情。

而使用spring的提供的数据库连接的好处也是显而易见的,很多数据库连接的问题,它都帮我们管理解决了,如关闭连接等。

猜你喜欢

转载自blog.csdn.net/topdeveloperr/article/details/85052710