jdbc和DataSource

文章转载于:Spring数据库原理-DataSource

jdbc 操作数据库

public class TestMark_to_win {
    public static void main(String[] args) throws java.sql.SQLException,
            ClassNotFoundException {
        java.sql.Connection connection = null;
        java.sql.Statement statement = null;
        java.sql.ResultSet resultSet = null;
        Class.forName("com.mysql.jdbc.Driver");
        connection = java.sql.DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/test", "root", "1234");
        statement = connection.createStatement();
        resultSet = statement.executeQuery("select * from login");
        while (resultSet.next()) {
            System.out.println(resultSet.getString("id") + "--"
                    + resultSet.getString("name"));
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

DataSource

引言

用Spring进行Web应用开发时,我们经常会做datasource的配置。而且datasource的配法风格各异。那么他们到底有哪些异同点呢?

image-20180811151925263

image-20180811152018356

image-20180811152412438

DataSource作用

DataSource是javax.sql包中的类,是Java原生rt.jar包中的类。

public interface DataSource  extends CommonDataSource, Wrapper {

  Connection getConnection() throws SQLException;

  Connection getConnection(String username, String password)
    throws SQLException;
}

javax.sql.DataSource定义的是抽象方法,通过Java JNDI的方式将具体实现开放给各个厂商、组织自己、个人自己实现。

在Spring框架中,通过DataSource + 配置的方式,来定义具体的数据库源。并向Spring框架提供数据源的Connection服务。

在Spring中若想实现多数据源,那么就需要在DataSource下手。

javax.sql.DataSource源码注释说明

/**
 * <p>A factory for connections to the physical data source that this
 * {@code DataSource} object represents.  An alternative to the
 * {@code DriverManager} facility, a {@code DataSource} object
 * is the preferred means of getting a connection. An object that implements
 * the {@code DataSource} interface will typically be
 * registered with a naming service based on the
 * Java&trade; Naming and Directory (JNDI) API.
 * <P>
 * The {@code DataSource} interface is implemented by a driver vendor.
 * There are three types of implementations:
 * <OL>
 *   <LI>Basic implementation -- produces a standard {@code Connection}
 *       object
 *   <LI>Connection pooling implementation -- produces a {@code Connection}
 *       object that will automatically participate in connection pooling.  This
 *       implementation works with a middle-tier connection pooling manager.
 *   <LI>Distributed transaction implementation -- produces a
 *       {@code Connection} object that may be used for distributed
 *       transactions and almost always participates in connection pooling.
 *       This implementation works with a middle-tier
 *       transaction manager and almost always with a connection
 *       pooling manager.
 * </OL>
 * <P>
 * A {@code DataSource} object has properties that can be modified
 * when necessary.  For example, if the data source is moved to a different
 * server, the property for the server can be changed.  The benefit is that
 * because the data source's properties can be changed, any code accessing
 * that data source does not need to be changed.
 * <P>
 * A driver that is accessed via a {@code DataSource} object does not
 * register itself with the {@code DriverManager}.  Rather, a
 * {@code DataSource} object is retrieved though a lookup operation
 * and then used to create a {@code Connection} object.  With a basic
 * implementation, the connection obtained through a {@code DataSource}
 * object is identical to a connection obtained through the
 * {@code DriverManager} facility.
 * <p>
 * An implementation of {@code DataSource} must include a public no-arg
 * constructor.
 *
 * @since 1.4
 */

概要翻译

  1. Part - 1:
    • DataSource是获取物理数据源连接的工厂类。
    • 作为DriverManager工具的替代方案,DataSource对象是获取连接的首选方法.
    • DataSource的实现类一般都通过JNDI的方式注册到框架中进行使用。
  2. Part - 2:
    • DataSource一般由数据库厂商提供对应的实现类,DataSource有三种实现方式
      1. 基本实现,生成标准连接对象。
      2. 连接池实现,适用于中间层连接池管理器。
      3. 分布式事务实现。此实现适用于中间层事务管理器,并且几乎总是使用连接池管理器。
  3. Part - 3:
    • DataSource向Spring框架屏蔽了具体数据源的差异,即当物理数据源切换时,只需要更新相关的DataSource配置值即可,不需要应用层修改代码。
  4. Part - 4:
    • 数据库Driver都是通过DataSource对象被注册到DriverManager中,而不是由Driver直接向DriverManager注册。
    • 但是对于获取Connection,先通过检索先获得DataSource,再根据DataSource对象进行getConnection,而不是直接从DriverManager获取Connection。

Spring-JDBC的DataSource实现案例

在这里插入图片描述

在 Spring-jdbc 下,DataSource 最顶级的类是 AbstractDataSource ,对 DataSource 的所有父接口方法做了实现。但保留 getConnection() 方法由子类实现。

在 AbstractDriverBasedDataSource 中,定义了大量的参数,诸如 url, username 等,这些都被用来定位并定义与数据库实例的连接。

package org.springframework.jdbc.datasource;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import org.springframework.lang.UsesJava7;
import org.springframework.util.Assert;

public abstract class AbstractDriverBasedDataSource extends AbstractDataSource {
    private String url;
    private String username;
    private String password;
    private String catalog;
    private String schema;
    private Properties connectionProperties;

    public AbstractDriverBasedDataSource() {
    }

    ......略

    public Connection getConnection() throws SQLException {
        return this.getConnectionFromDriver(this.getUsername(), this.getPassword());
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return this.getConnectionFromDriver(username, password);
    }

    @UsesJava7
    protected Connection getConnectionFromDriver(String username, String password) throws SQLException {
        Properties mergedProps = new Properties();
        Properties connProps = this.getConnectionProperties();
        if(connProps != null) {
            mergedProps.putAll(connProps);
        }

        ......略

        return con;
    }

    protected abstract Connection getConnectionFromDriver(Properties var1) throws SQLException;
}

整合方案为将除 url 外的所有参数整合在同一个 Properties 对象中 (其中,Properties 可以被认为是一个线程安全的 Hash Map) 。最终调用 Connection getConnectionFromDriver(Properties props) 获取连接。

AbstractDriverBasedDataSource 抽象类的两个子类 DriverManagerDataSource 和 SimpleDriverDataSource 都以不同方式获得了连接(Connection),但总结而言,获取连接(Connection) 的任务被委托给了 Driver 来实现。

// ----------------------------
// SimpleDriverDataSource 的实现
// ----------------------------
@Override
protected Connection getConnectionFromDriver(Properties props) throws SQLException {
    Driver driver = getDriver();
    String url = getUrl();
    Assert.notNull(driver, "Driver must not be null");
    if (logger.isDebugEnabled()) {
        logger.debug("Creating new JDBC Driver Connection to [" + url + "]");
    }
    return driver.connect(url, props);
}

// -----------------------------
// DriverManagerDataSource 的实现
// -----------------------------
@Override
protected Connection getConnectionFromDriver(Properties props) throws SQLException {
    String url = getUrl();
    Assert.state(url != null, "'url' not set");
    if (logger.isDebugEnabled()) {
        logger.debug("Creating new JDBC DriverManager Connection to [" + url + "]");
    }
    // 调了个内部函数
    return getConnectionFromDriverManager(url, props);
}

protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
    // 委托给 DriverManager 类来获取连接
    // DriverManager 的主要操作是遍历在该管理类中注册的 Driver
    // 每个 Driver 实例都去尝试一下,能不能获得一个连接
    // 第一次在某个 Driver 中拿到一个连接即返回连接 (Connection)
    return DriverManager.getConnection(url, props);
}

Why do we use a DataSource instead of a DriverManager?

Below code shows two way for getting connection.

There is no need to know about URL in case of mySqlDataSource as this line is commented.

public class MySqlDataSourceTest {

public static void main(String[] args) throws SQLException, ClassNotFoundException {


    /************** using MysqlDataSource starts **************/
    MysqlDataSource d = new MysqlDataSource();
    d.setUser("root");
    d.setPassword("root");
//  d.setUrl("jdbc:mysql://localhost:3306/manavrachna");
    d.setDatabaseName("manavrachna");
    Connection c =  (Connection) d.getConnection();
    /************** using MysqlDataSource ends**************/


    /************** using DriverManager start **************/
    Class.forName("com.mysql.jdbc.Driver");
    Connection c=(Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/manavrachna","root","root");
    /************** using DriverManager ends **************/

    Statement st=(Statement) c.createStatement();
    ResultSet rs=st.executeQuery("select id from employee");
    while(rs.next())
    {
        System.out.println(rs.getInt(1));
    }

}

}

DataSource objects can provide connection pooling and distributed transactions, so you may have to use DataSource if you need one of or both these features.

We can get connection using a datasource as follows. Use the connection to perform any database query.

DataSource datasource = (DataSource) new InitialContext().lookup(dataSourceName);
Connection connection = datasource.getConnection();

DriverManager

  • hampers the application performance as the connections are created/closed in java classes.
  • does not support connection pooling.

DataSource

  • improves application performance as connections are not created/closed within a class, they are managed by the application server and can be fetched while at runtime.
  • it provides a facility creating a pool of connections
  • helpful for enterprise applications

更好的可扩展性和维护性

因为DriverManager您需要知道所有详细信息(主机、端口、用户名、密码、驱动程序类)才能连接到数据库并获取连接。将属性文件中的内容外部化并不会改变您需要了解它们的事实。

使用 aDataSource您只需要知道 JNDI 名称。AppServer 关心细节并且不是由客户端应用程序的供应商配置,而是由托管应用程序的管理员配置。

可扩展性:

假设您需要自己创建连接,您将如何处理不断变化的负载,有时您有 10 个用户,有时您有 1000 个,您不能只在需要时获得连接,然后“释放”它,这样数据库服务器就不会退出连接,这会导致您进入连接池。DriverManager不提供,提供DataSource。

如果您要自己编程连接池,则必须使用DriverManager,否则使用DataSource.


  • DataSource是作为DriverManager的替代品而推出的,DataSource 对象是获取连接的首选方法。

    为何放弃DriverManager

    DriverManager负责管理驱动程序,并且使用已注册的驱动程序进行连接。

  //1、注册驱动
  Class.forName("com.mysql.jdbc.Driver");
  //数据库连接所需参数
  String user = "root";
  String password = "123456";
  String url = "jdbc:mysql://localhost:3306/sampledb?useUnicode=true&characterEncoding=utf-8";
  //2、获取连接对象
  Connection conn = DriverManager.getConnection(url, user, password);
  使用DriverManager的一般形式如上面代码所示

直接使用DriverManager的这种形式,通常需要将驱动程序硬编码到项目中(JDBC4.0后可以自动注册驱动程序)

而且最重要的是DriverManager的getConnection方法获取的连接,是建立与数据库的连接,是建立与数据库的连接,是建立与数据库的连接。

但是建立与数据库的连接是一项较耗资源的工作,频繁的进行数据库连接建立操作会产生较大的系统开销。

随着企业级应用复杂度的提升以及对性能要求的提高,这一点是难以接受的。

连接池

既然每次使用时都重新建立与数据库之间的连接,会产生较大的系统开销

是否可以事先创建一些连接备用,当需要时,从这些连接中选择一个提供出去;当连接使用完毕后,并不是真正的关闭,而是将这些数据状态还原,然后继续等待下一个人使用?

比如滑雪场会租赁雪具滑雪服等,如果你不是资深玩家,你没有必要浪费钱买,即使你不差钱,每次去滑雪场都不能轻装上阵,每次都要携带很多装备,也是一件麻烦事。

这种没必要的花费或者麻烦其实都是一种开销。

连接池的核心与租用的理念有类似的点,重复使用可以提高连接的利用率,减少开销(当然连接池的使用并不需要你花费一笔租金)

连接的持有是消耗空间的,但是现在绝大多数场景下,磁盘空间并没有那么金贵,我们更关心的是性能,所以空间换取时间,连接池的逻辑被广泛应用。

数据源

DriverManager只是建立与数据库之间的连接,如何才能将连接池的概念应用其中?

一种很自然的方式就是提供一个薄层的封装,建立一个中间层,这个中间层将DriverManager生成的连接,组织到连接池中,然后从池中提供连接。

在这里插入图片描述

Data Source就是DriverManager的一种替代角色,对外呈现就类似于一个DriverManager,拥有对外提供连接的能力

直接使用DriverManager,驱动程序与管理器是“服务者—管理者”的形式,借助于管理者才能提供服务。

Data Source将驱动程序的概念淡化了,突出驱动程序能够提供的服务与能力,将驱动程序提供的服务与能力抽象为Data Source数据源这一角色。

DataSource中获取的连接来自于连接池中,而池中的连接根本也还是从DriverManager获取而来

有了数据源这一中间层,就可以实现连接池和分布式事务的管理。

对外呈现DataSource就是类似于DriverManager的一个存在。

核心架构

关于数据源有以下几个核心的接口

CommonDataSource接口定义了 DataSource、XADataSource 和 ConnectionPoolDataSource 之间公用的方法。

DataSource 是 官方定义的获取 connection 的接口, ConnectionPoolDataSource 是官方定义的从 connection pool 中拿 connection 的接口,XADataSource是定义的用来获取分布式事务连接的

DataSource

这是一个工厂对象,用于提供到此 DataSource 对象所表示的物理数据源的连接。

作为 DriverManager 工具的替代项,DataSource 对象是获取连接的首选方法。

实现 DataSource 接口的对象通常在基于 JavaTM Naming and Directory Interface (JNDI) API 的命名服务中注册。

DataSource 接口由驱动程序供应商实现。共有三种类型的实现:

基本实现 - 生成标准的 Connection 对象
连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
Java作为一种广泛使用的开发语言,自然不需要我们自己实现DataSource,一些大厂已经帮我们实现了

比如:DBCP ,C3P0 ,druid

目前推荐使用ALI的Druid,http://druid.io/

maven中央仓库: http://central.maven.org/maven2/com/alibaba/druid/

Druid是一个开源项目,源码托管在github上,源代码仓库地址是 https://github.com/alibaba/druid。

数据源作为DriverManager的替代者,用于获取数据库连接,你应该总是使用DataSource

DataSource是应用程序与数据库连接的一个抽象的中间层,是一个接口

对于DataSource已经有了很多优秀的实现,其中较为突出的为Druid,建议使用,Druid不仅仅提供了连接池的功能还提供了其他比如监控等功能,非常强大。

对于数据源的应用,除了用户名密码url还有其他的一些属性信息,比如最大连接数,建立连接的最大等待时间等,不同的连接池略微有出入,可以查看手册。

对于DataSource的一些实现,经常被叫做数据库连接池,比如Druid官方文档中说“Druid是Java语言中最好的数据库连接池“,本质核心就是DataSource的一个实现类,作为中间层使用,并且基本上都提供了附带的其他的服务,也就是说不仅仅实现了核心建筑,也基于核心之上构建了很多的外围建设。

原文链接:https://blog.csdn.net/weixin_47277897/article/details/120949139

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个关于Java Spring框架中配置JDBC连接DataSource的问题。在Spring中,我们可以使用以下配置来连接数据源: 1. 配置数据源 ``` <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> ``` 在这个配置中,我们使用了`DriverManagerDataSource`来创建数据源,你也可以使用其他的数据源,比如`BasicDataSource`、`C3P0DataSource`、`HikariDataSource`等。 2. 配置JdbcTemplate ``` <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> ``` 在这个配置中,我们使用了`JdbcTemplate`来执行SQL语句,这个类提供了一系列的方法,比如`queryForObject`、`queryForList`、`update`等,可以方便地执行SQL语句。 3. 使用JdbcTemplate ``` @Autowired private JdbcTemplate jdbcTemplate; public void someMethod() { String sql = "SELECT * FROM user WHERE id=?"; User user = jdbcTemplate.queryForObject(sql, new Object[]{1}, new BeanPropertyRowMapper<>(User.class)); } ``` 在这个示例中,我们使用了`JdbcTemplate`的`queryForObject`方法来执行SQL语句,并将结果转换为一个`User`对象。 希望这个回答能够帮到你,如果还有问题,可以继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值