【java学习】数据库连接:JDBC与JdbcTemplate

数据库学习 专栏收录该内容
9 篇文章 1 订阅

1,概念

1)数据库连接

java应用程序可以通过JDBC或Hibernate对数据库系统进行访问。JDBC或Hibernate提供了事务控制的接口,这些接口把事务控制相关的命令发送给数据库系统,由数据库系统来控制事务的隔离级别。
一般来说,java 应用程序访问数据库的过程是:

  1. 加载数据库驱动程序;
  2. 通过jdbc 建立数据库连接;
  3. 访问数据库,执行sql 语句;
  4. 断开数据库连接。

2)JDBC(Java Data Base Connectivity,java数据库连接)

是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC是由一系列连接(Connection)、SQL语句(Statement)和结果集(ResultSet)构成的,其主要作用概括起来有如下3个方面: 连接(建立与数据库的连接)、查询(向数据库发起查询请求)、反馈(处理数据库返回结果)。

当使用JDBC时,通常会在数据访问层使用如下循环模式:

try{
	//obtain database connetction
	//start a transaction
	//create and excute the query
	//process query result
	//commit the transaction
}catch(SQLException e){
	//handle SQL exceptions, perform tansaction rollback
}finally{
	//close db resources like connections, statements
}

在使用JDBC时,必须自己来管理数据库资源如:获取PreparedStatement,设置SQL语句参数,关闭连接等步骤。为了方便使用采用JdbcTemplate进行JDBC的使用。

3)Hibernate

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使java程序员可以随心所欲地使用对象编程思想来操纵数据库。目前使用较少。

4)JdbcTemplate

​JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate处理了资源的建立和释放。他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。

5)sql编译与Statement接口

Statement

①Statement

Statement是 Java 执行数据库操作的一个重要接口,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。Statement对象,用于执行不带参数的简单SQL语句。
Statement 每次执行sql语句,数据库都要执行sql语句的编译 ,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement.

在JDBC应用中,应该尽可能的以PreparedStatement代替Statement。

Statement sta=con.createStatement();
ResultSet rst=sta.executeQuery(“select * from book”);
方法含义
executeQuery(String sql)用于向数据发送查询语句。
executeUpdate(String sql)用于向数据库发送insert、update或delete语句
execute(String sql)用于向数据库发送任意sql语句
addBatch(String sql)把多条sql语句放到一个批处理中。
executeBatch()向数据库发送一批sql语句执行。

②PreparedStatement

PreparedStatement继承Statement,是预编译的,优点:

  1. 在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高
    因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。
  2. 安全性高
    有效防止Sql注入等问题。
  3. 提高性能
    对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch;
  4. 代码的可读性和可维护性。
PreparedStatement pst=con.prepareStatement(“select * from users where name=? and password=?);
pst.setString(1, username);
pst.setString(2, password);
ResultSet rst=pst.executeQuery();

③CallableStatement

CallableStatement接口扩展了 PreparedStatement(父接口),用来调用存储过程,它提供了对输出和输入/输出参数的支持。CallableStatement 接口还具有对 PreparedStatement 接口提供的输入参数的支持.

6)获取结果ResultSet

Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式,ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。(ResultSet跟普通的数组不同,索引从1开始而不是从0开始);

①获取行

ResultSet提供了对结果集进行滚动的方法:

next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
    ResultSet rs = null;
    //4.向数据库发sql,并获取代表结果集的resultset
    String sql = "select id,name,password,email,birthday from users";
    rs = st.executeQuery(sql);
    			
    //5.取出结果集的数据
    rs.afterLast();
    rs.previous();
    System.out.println("id=" + rs.getObject("id"));
    System.out.println("name=" + rs.getObject("name"));
    System.out.println("password=" + rs.getObject("password"));
    System.out.println("email=" + rs.getObject("email"));
    System.out.println("birthday=" + rs.getObject("birthday"));

②获取值

ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:

// 获取任意类型的数据
getObject(int index)
getObject(string columnName)

//获取指定类型的数据,例如:
getString(int index)
getString(String columnName)

2,JdbcTemplate语法

1)管理JDBC连接:DataSource

在 JDBC API中,获取数据库连接方式有:DriverManager、DataSource(JdbcTemplate采用这种方式)。

DriverManager缺点:

  1. 频繁的建立数据库连接与断开数据库,这样会消耗大量的资源和时间,降低性能。
  2. 数据库的连接需要用户名和密码等等,这些需要一定的内存和CPU一定开销。

DataSource(数据源):
它负责建立与数据库的连接,当在应用程序中访问数据库时不必编写连接数据库的代码,直接引用DataSource获取数据库的连接对象即可。用于获取操作数据Connection对象。
数据源建立多个数据库连接,这些数据库连接会保存在数据库连接池中,当需要访问数据库时,只需要从数据库连接池中获取空闲的数据库连接,当程序访问数据库结束时,数据库连接会放回数据库连接池中。

①DriverManagerDataSource配置

不要在生产环境中使用DriverManagerDataSource ,因为它没有连接池功能。只要使用该类都会打开一个新的物理jdbc连接,比较消耗资源。

public class Ch4Configuration{

	@Bean
	public DataSource dataSource(){
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName("org.h2.Driver");
		dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
		dataSource.setUsername("sa");
		dataSource.setPassword("");
		return dataSource;
	}	
}

②连接池化的数据源:C3P0、Apache Commons DBCP

C3P0

C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和 jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。

C3P0数据源配置:
配置 C3P0 信息

  • application.properties
# SQLServer 数据库配置信息
c3p0.jdbcUrl=jdbc:sqlserver://localhost:1433;DatabaseName=game
c3p0.user=gm
c3p0.password=root
c3p0.driverClass=com.microsoft.sqlserver.jdbc.SQLServerDriver
c3p0.minPoolSize=2
c3p0.maxPoolSize=10
c3p0.maxIdleTime=30
c3p0.checkoutTimeout=30000
c3p0.acquireIncrement=3
c3p0.maxStatements=1000
c3p0.initialPoolSize=3
c3p0.idleConnectionTestPeriod=60
c3p0.acquireRetryAttempts=30
c3p0.acquireRetryDelay=1000
c3p0.breakOnAcquireFailure=true
c3p0.breakAfterAcquireFailure=false
c3p0.testConnectionOnCheckout=false

引入 C3P0 和 SQLServer 依赖:

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>mssql-jdbc</artifactId>
    <version>7.0.0.jre8</version>
</dependency>
<!-- 引入 dbutils 依赖操作数据库 -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.6</version>
</dependency>

创建 C3P0 配置类:

@Configuration
public class C3p0Configuration {

    @Bean(name = "dataSource")
    @Primary // 用 @Primary 区分主数据源
    @ConfigurationProperties(prefix = "c3p0") // 指定配置文件中,前缀为 c3p0 的属性值
    public DataSource dataSource(){
        return DataSourceBuilder.create()
                .type(ComboPooledDataSource.class).build();
    }
}

C3P0 多数据源配置:

@Configuration
@ConfigurationProperties(prefix = "spring.data.neo4j")
public class DataSourceConfig {
	String driverClassName;
    String jdbcUrl;
    String username;
    String password;
    String type;
	@Bean(name = "neo4jDataSource")
    public DataSource neo4jDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds =  new ComboPooledDataSource();
        ds.setDriverClass(driverClassName);
        ds.setUser(username);
        ds.setPassword(password);
        ds.setJdbcUrl(jdbcUrl);
        //根据实际需要配置各种参数。
        return ds;
    }
}

yml文件中配置:
spring
   data:
    neo4j:
      driver-class-name: org.neo4j.jdbc.Driver
      jdbc-url: jdbc:neo4j:bolt://postgresql.host:7687
      uri: bolt://postgresql.host:7687
      username: neo4j
      password: ${spring.data.neo4j.password}
      type: ${spring.datasource.type}

//使用Spring的注入功能,可以把DataSource(数据库连接池)注册到jdbcTemplate中。
@Configuration
@EnableTransactionManagement
public class Neo4jJdbcConfig {

    @Autowired
    //Qualifier指明具体的实现类
    @Qualifier("neo4jDataSource")
    DataSource neo4jDataSource;

    @Bean(name = "neo4jJdbcTemplate")
    public JdbcTemplate neo4jJdbcTemplate() {
        return new JdbcTemplate(neo4jDataSource);
    }
}

//使用JdbcTemplate时,如果是多数据源
	@Autowired
    @Qualifier("neo4jJdbcTemplate")
    private JdbcTemplate neo4jJdbcTemplate;

DBCP

@Configuration
public class DataSourceConfig {

        BasicDataSource ds =  new BasicDataSource ();
	   	//其它与C3P0一样
        return ds;
}
常见属性说明
initialSize=“10”初始化连接,连接池启动时创建的初始化连接数量(默认值为0)
maxActive=“80”最大活动连接,连接池中可同时连接的最大的连接数(默认值为8)
minIdle=“10”最小空闲连接,连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接,类似于jvm参数中的Xmn设置)
maxIdle=“60”最大空闲连接,连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)
maxWait=“3000”从池中取连接的最大等待时间,单位ms.当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限)

注意事项:
maxIdle值与maxActive值应配置的接近:
当连接数超过maxIdle值后,刚刚使用完的连接(刚刚空闲下来)会立即被销毁。而不是想要的空闲M秒后再销毁起一个缓冲作用。若maxIdle与maxActive相差较大,在高负载的系统中会导致频繁的创建、销毁连接,连接数在maxIdle与maxActive间快速频繁波动,这不是想要的。高负载系统的maxIdle值可以设置为与maxActive相同或设置为-1(-1表示不限制),让连接数量在minIdle与maxIdle间缓冲慢速波动。

timeBetweenEvictionRunsMillis建议设置值:
minIdle要与timeBetweenEvictionRunsMillis配合使用才有用,单独使用minIdle不会起作用。

initialSize=“5”,会在tomcat一启动时,创建5条连接,效果很理想。但同时我们还配置了minIdle=“10”,也就是说,最少要保持10条连接,那现在只有5条连接,哪什么时候再创建少的5条连接呢?
1、等业务压力上来了, DBCP就会创建新的连接。
2、配置timeBetweenEvictionRunsMillis=“时间”,DBCP会启用独立的工作线程定时检查,补上少的5条连接。销毁多余的连接也是同理。

连接销毁的逻辑

DBCP的连接数会在initialSize - minIdle - maxIdle - maxActive 之间变化。变化的逻辑描述如下:
默认未配置initialSize(默认值是0)和timeBetweenEvictionRunsMillis参数时,刚启动tomcat时,连接数是0。当应用有一个并发访问数据库时DBCP创建一个连接。目前连接数量还未达到minIdle,但DBCP也不自动创建新连接已使数量达到minIdle数量(没有一个独立的工作线程来检查和创建)。随着应用并发访问数据库的增多,连接数也增多,但都与minIdle值无关,很快minIdle被超越,minIdle值一点用都没有。直到连接的数量达到maxIdle值,这时的连接都是只增不减的。 再继续发展,连接数再增多并超过maxIdle时,使用完的连接(刚刚空闲下来的)会立即关闭,总体连接的数量稳定在maxIdle但不会超过maxIdle。
但活动连接(在使用中的连接)可能数量上瞬间超过maxIdle,但永远不会超过maxActive。这时如果应用业务压力小了,访问数据库的并发少了,连接数也不会减少(没有一个独立的线程来检查和销毁),将保持在maxIdle的数量。
默认未配置initialSize(默认值是0),但配置了timeBetweenEvictionRunsMillis=“30000”(30秒)参数时,刚启动tomcat时,连接数是0。马上应用有一个并发访问数据库时DBCP创建一个连接。目前连接数量还未达到minIdle,每30秒DBCP的工作线程检查连接数是否少于minIdle数量,若少于就创建新连接直到达到minIdle数量。
随着应用并发访问数据库的增多,连接数也增多,直到达到maxIdle值。这期间每30秒DBCP的工作线程检查连接是否空闲了30分钟,若是就销毁。但此时是业务的高峰期,是不会有长达30分钟的空闲连接的,工作线程查了也是白查,但它在工作。到这里连接数量一直是呈现增长的趋势。
当连接数再增多超过maxIdle时,使用完的连接(刚刚空闲下来)会立即关闭,总体连接的数量稳定在maxIdle。停止了增长的趋势。但活动连接(在使用中的连接)可能数量上瞬间超过maxIdle,但永远不会超过maxActive。
这时如果应用业务压力小了,访问数据库的并发少了,每30秒DBCP的工作线程检查连接(默认每次查3条)是否空闲达到30分钟(这是默认值),若连接空闲达到30分钟,就销毁连接。这时连接数减少了,呈下降趋势,将从maxIdle走向minIdle。当小于minIdle值时,则DBCP创建新连接已使数量稳定在minIdle,并进行着新老更替。

2)执行SQL语句

在JdbcTemplate中执行SQL语句的方法大致分为3类:

execute

可以执行所有SQL语句,因为没有返回值,一般用于执行DDL语句。

queryXxx

用于DQL数据查询语句。

查询单行:

String sql = "select num,name,age from student where id = ?";
    RowMapper<stu> rowMapper = new BeanPropertyRowMapper<stu>(stu.class);
    stu s = jdbcTemplate.queryForObject(sql, rowMapper,5);//最后一个参数为id值

查询多行:

String sql = "select num,name,age from student where id > ?";
    RowMapper<stu> rowMapper = new BeanPropertyRowMapper<stu>(stu.class);
    List<stu> s = jdbcTemplate.query(sql, rowMapper,0);//最后一个参数为id值

单值查询:

String sql = "select count(name) from student";
    Long count = jdbcTemplate.queryForObject(sql, Long.class);

update

用于执行INSERT、UPDATE、DELETE等DML语句。

 String sql = "update student set name = ? where num = ?";
    jdbcTemplate.update(sql, "pink",0001);

批量插入:batchUpdate

在数据库地址上加上rewriteBatchedStatements=true 开启批量写入;批处理操作可以减少往返数据库的次数,提高数据库访问性能。

import org.springframework.jdbc.core.JdbcTemplate;

@Autowired
JdbcTemplate jdbcTemplate;

public int[] updateOrSaveBatch(String updateOrInsertSql, List<UserPo> userList) {
        return jdbcTemplate.batchUpdate(updateOrInsertSql,
                new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        ps.setObject(1, userList.get(i).getId());
                        ps.setObject(2, userList.get(i).getName());
                    }
                    @Override
                    public int getBatchSize() {
                        return userList.size();
                    }
                });
    }

3,demo

1)jdbc

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JdbcConnection {
    public void connection() {
        PreparedStatement preparedStatement = null;
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            //初始化对象
            //1.加载驱动 不同的数据库对应的驱动不同,同时需要引入lib/*.jar包或添加对应的maven依赖
            Class.forName("org.postgresql.Driver");
            //获取数据库连接。三个参数为:url、user、psw  不同数据库url格式不同
            connection = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5432/databaseName", "postgres", "psw");
            /**
             * 执行对应的sql语句
             * connection常用的方法有:
             * 1. createStatement()	创建向数据库发送sql的statement对象。
             * 2. prepareStatement(sql)	创建向数据库发送预编译sql的PrepareSatement对象。
             * 3. prepareCall(sql)	创建执行存储过程的callableStatement对象。
             * 4. setAutoCommit(boolean autoCommit)	设置事务是否自动提交。
             * 5. commit()	在链接上提交事务。
             * 6. rollback()	在此链接上回滚事务。
             */
            preparedStatement = connection.prepareStatement("create database lwhtest");

            /**
             *
             */
            boolean result = preparedStatement.execute();
            if (result) {
                System.out.println("create a connection success!");
            } else {
                System.out.println("create a connection fail!");
            }
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("error!");
        } finally {
            //释放资源:ResultSet, Statement和Connection对象
            try {
                if (preparedStatement != null)
                    preparedStatement.close();
                if (connection != null)
                    connection.close();
            } catch (Exception e2) {
                // TODO: handle exception
            }
        }
    }
}

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

兔兔西

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值