zoukankan      html  css  js  c++  java
  • Spring的数据库编程

    一、传统的JDBC编程

    JDBC 我们一定不陌生,刚开始学习的时候,我们写过很多很多重复的模板代码。

    pom.xml

    <!-- mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    创建 DBUtil 类

    第一步我们可以把重复的模板代码提出来创建一个【DBUtil】数据库工具类:

    public final class DBUtil {
        private DBUtil(){}
        private static final String DB_IP = "127.0.0.1";
        private static final int DB_PORT = 3305;
        private static final String DATABASE = "spring";
        private static final String ENCODING = "UTF-8";
        private static final String LOGIN_NAME = "root";
        private static final String PASSWORD = "123456";
    
        static {
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getConnection() throws SQLException {
            String url = String.format("jdbc:mysql://%s:%d/%s?useSSL=false&&characterEncoding=%s", DB_IP, DB_PORT, DATABASE, ENCODING);
            return DriverManager.getConnection(url, LOGIN_NAME, PASSWORD);
        }
    
    }
    使用 try-catch 语句自动关闭资源
    public class StudentServiceImpl {
    
        public Student getOne(int id) {
    
            String sql = "SELECT ID,NAME FROM SP_USER WHERE id = ?";
            Student student = null;
            // 将 JDBC 声明变量包含在 try(..) 里将自动关闭资源
            try (Connection con = DBUtil.getConnection(); PreparedStatement ps = con.prepareStatement(sql)) {
    
                // 设置参数
                ps.setInt(1, id);
                // 执行SQL
                ResultSet rs = ps.executeQuery();
                // 组装结果集返回 POJO
                if (rs.next()) {
                    student = new Student();
                    student.setId(rs.getInt(1));
                    student.setName(rs.getString(2));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return student;
        }
    }

    其中Student实体类如下:

    public class Student {
        private Integer id;
        private String name;
        private Integer age;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }

    二、Spring中的JDBC

    要想使用 Spring 中的 JDBC 模块,就必须引入相应的 jar 文件:

    <!-- Spring Jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>

    配置数据源有两种:

    • 使用Spring内置的简单数据库配置
    • 使用第三方数据库连接池
    Spring内置数据库配置

    Spring的内置类是org.springframework.jdbc.datasource.SimpleDriverDataSource

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import javax.sql.DataSource;
    
    public class StudentSv {
    
        private DataSource dataSource;
    
        public void setDataSource(DataSource dataSource) throws SQLException {
            this.dataSource = dataSource;
        }
    
        public Student getOne(int id) {
    
            String sql = "SELECT ID,NAME FROM SP_USER WHERE id = ?";
            Student student = null;
            // 将 JDBC 声明变量包含在 try(..) 里将自动关闭资源
            try (Connection con = this.dataSource.getConnection(); PreparedStatement ps = con.prepareStatement(sql)) {
    
                // 设置参数
                ps.setInt(1, id);
                // 执行SQL
                ResultSet rs = ps.executeQuery();
                // 组装结果集返回 POJO
                if (rs.next()) {
                    student = new Student();
                    student.setId(rs.getInt(1));
                    student.setName(rs.getString(2));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return student;
        }
    }

    xml配置内容如下:

    <bean id="dateSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3305/spring?useSSL=false&amp;characterEncoding=UTF-8"/>
    </bean>
    
    <bean id="studentService" class="com.codedot.db.StudentSv">
        <property name="dataSource" ref="dateSource"></property>
    </bean>

    测试

    public class DBTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            StudentSv studentSv = context.getBean(StudentSv.class);
            Student student = studentSv.getOne(1);
            System.out.println(student.getName());
        }
    }
    使用第三方数据源

    上面配置的这个简单的数据源一般用于测试,因为它不是一个数据库连接池,只是一个很简单的数据库连接的应用。在更多的时候,我们需要使用第三方的数据库连接。

    常用的有dbcp2、C3p0、Druid。

    为什么要使用数据库连接池 、好处是什么?

    ① 资源重用 (连接复用):避免了频繁创建、释放连接引起的大量性能开销。增进系统的平稳性。 

    ② 更快的系统响应速度:避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。 

    ③ 统一的连接管理,避免数据库连接泄露

    dbcp2数据源

    全限定名:org.apache.commons.dbcp2.BasicDataSource
    需要添加:commons-dbcp2-xxx.jar、commons-pool2-xxx.jar
    注意; dbcp2需要jdk1.7,否则会报错:Unsupported major.minor version 51.0

    pom.xml

    <!-- dbcp2 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.7.0</version>
    </dependency>

    dbcp2.properties

    # BasicDataSource + mysql
    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3305/spring?useSSL=false&characterEncoding=UTF-8
    jdbc.username=root
    jdbc.password=123456
    jdbc.maxTotal=100
    jdbc.initialSize=10
    jdbc.maxWaitMillis=60000
    jdbc.minIdle=10
    jdbc.maxIdle=15
    jdbc.logAbandoned=true
    jdbc.removeAbandoned=true
    jdbc.removeAbandonedTimeout=10
    jdbc.timeBetweenEvictionRunsMillis=10000
    jdbc.numTestsPerEvictionRun=10
    jdbc.minEvictableIdleTimeMillis=10000
    jdbc.validationQuery=select 1
    jdbc.testWhileIdle=true
    jdbc.testOnBorrow=true
    jdbc.defaultAutoCommit=true
    jdbc.defaultReadOnly=false

    xml配置

    <!-- 加载属性文件 -->
        <!-- 第一种方式 -->
       <!--  <bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <value>classpath:dbcp2.properties</value>
            </property>
         </bean>-->
        <!-- 第二种方式, 多个properties可以使用逗号隔开,即classpath:xxx, classpath:xxxx -->
        <context:property-placeholder location="classpath:dbcp2.properties" />
        <!-- dbcp2数据源 -->
        <bean id="dataSource" class="org.apache.commons.dbcp2.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}" />
            <!--maxActive: 最大连接数量 -->
            <property name="maxTotal" value="${jdbc.maxTotal}" />
            <!--minIdle: 最小空闲连接 -->
            <property name="minIdle" value="${jdbc.minIdle}" />
            <!--maxIdle: 最大空闲连接 -->
            <property name="maxIdle" value="${jdbc.maxIdle}" />
            <!--initialSize: 初始化连接 -->
            <property name="initialSize" value="${jdbc.initialSize}" />
            <!-- 连接被泄露时是否打印 -->
            <property name="logAbandoned" value="${jdbc.logAbandoned}" />
            <!-- removeAbandonedOnBorrow: 是否自动回收超时连接 -->
            <property name="removeAbandonedOnBorrow" value="${jdbc.removeAbandoned}" />
            <!-- removeAbandonedTimeout: 超时时间(以秒数为单位) -->
            <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
            <!-- maxWaitMillis: 从连接池获取一个连接时,最大的等待时间, 超时等待时间以毫秒为单位 1000等于60秒 -->
            <property name="maxWaitMillis" value="${jdbc.maxWaitMillis}" />
            <!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
            <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
            <!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
            <property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}" />
            <!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
            <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
            <property name="validationQuery" value="${jdbc.validationQuery}" />
            <!-- 定时对线程池中的链接进行validateObject校验,对无效的链接进行关闭 -->
            <!-- <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/> -->
            <!-- 指定在从连接池中拿连接时,要检查连接是否有效,若无效会将连接从连接池中移除掉 -->
            <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
    
            <!-- defaultAutoCommit,通过这个池创建连接的默认自动提交状态。如果不设置,则setAutoCommit 方法将不被调用 -->
            <!-- <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}" /> -->
            <!-- defaultReadOnly, 通过这个池创建连接的默认只读状态。如果不设置,则setReadOnly 方法将不被调用。(部分驱动不支持只读模式,如:Informix) -->
            <property name="defaultReadOnly" value="${jdbc.defaultReadOnly}" />
            <!-- defaultTransactionIsolation,NONE/READ_COMMITTED/READ_UNCOMMITTED/REPEATABLE_READ/SERIALIZABLE -->
    
        </bean>
    
        <bean id="studentService" class="com.codedot.db.StudentSv">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    C3p0数据源

    全限定名:com.mchange.v2.c3p0.ComboPooledDataSource
    需要添加:c3p0-xxx.jar、mchange-commons-java-xxx.jar

    pom.xml

    <!-- C3p0 -->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.5</version>
    </dependency>
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>mchange-commons-java</artifactId>
        <version>0.2.19</version>
    </dependency>

    c3p0.properties

    # ComboPooledDataSource + mysql
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql://localhost:3305/spring?useSSL=false&characterEncoding=UTF-8
    jdbc.user=root
    jdbc.password=123456
    jdbc.minPoolSize=1
    jdbc.maxPoolSize=100
    jdbc.initialPoolSize=5
    jdbc.maxIdleTime=60
    jdbc.acquireIncrement=10
    jdbc.maxStatements=10
    jdbc.idleConnectionTestPeriod=30
    jdbc.acquireRetryAttempts=30
    jdbc.breakAfterAcquireFailure=true
    jdbc.testConnectionOnCheckout=false
    jdbc.automaticTestTable=true
    jdbc.checkoutTimeout=15000
    jdbc.numHelperThreads=10
    jdbc.testConnectionOnCheckin=true

    xml配置

     <context:property-placeholder location="classpath:c3p0.properties" />
        <!-- c3p0数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
              destroy-method="close">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
            <!--minPoolSize:连接池中保留的最小连接数。 -->
            <property name="minPoolSize" value="${jdbc.minPoolSize}" />
            <!--maxPoolSize:连接池中保留的最大连接数。Default: 15 -->
            <property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
            <!--initialPoolSize:初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
            <property name="initialPoolSize" value="${jdbc.initialPoolSize}" />
            <!--maxIdleTime:最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
            <property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
            <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
            <property name="acquireIncrement" value="${jdbc.acquireIncrement}" />
            <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
                如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
            <property name="maxStatements" value="${jdbc.maxStatements}" />
            <!-- maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存Statement数。默认为0; -->
    
            <!--idleConnectionTestPeriod:每60秒检查所有连接池中的空闲连接。Default: 0 -->
            <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}" />
            <!--acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
            <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}" />
            <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
                获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->
            <property name="breakAfterAcquireFailure" value="${jdbc.breakAfterAcquireFailure}" />
            <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
                等方法来提升连接测试的性能。Default: false -->
            <property name="testConnectionOnCheckout" value="${jdbc.testConnectionOnCheckout}" />
    
            <!-- automaticTestTable:C3P0将建一张名为Test的空表,并使用其自带的查询语句进行测试。 -->
            <property name="automaticTestTable" value="${jdbc.automaticTestTable}" />
            <!-- checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0; -->
            <property name="checkoutTimeout" value="${jdbc.checkoutTimeout}" />
            <!-- numHelperThreads:C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3; -->
            <property name="numHelperThreads" value="${jdbc.numHelperThreads}" />
            <!-- testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。 -->
            <property name="testConnectionOnCheckin" value="${jdbc.testConnectionOnCheckin}" />
        </bean>
    
        <bean id="studentService" class="com.codedot.db.StudentSv">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    Druid数据源

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,号称是目前最好的连接池。

    全限定名:com.alibaba.druid.pool.DruidDataSource
    需要添加:druid-xxx.jar、commons-logging-1.2.jar

    pom.xml

    <!-- druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.23</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>

    druid.properties

    # DruidDataSource + mysql
    jdbc.name=druid-1
    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3305/spring?useSSL=false&characterEncoding=UTF-8
    jdbc.username=root
    jdbc.password=123456
    jdbc.initialSize=1
    jdbc.maxActive=10
    jdbc.minIdle=1
    jdbc.maxWait=10000
    jdbc.poolPreparedStatements=true
    jdbc.maxOpenPreparedStatements=20
    jdbc.validationQuery=select 1
    jdbc.testOnBorrow=true
    jdbc.testOnReturn=false
    jdbc.testWhileIdle=true
    jdbc.timeBetweenEvictionRunsMillis=60000
    jdbc.minEvictableIdleTimeMillis=300000
    jdbc.filters=stat
    jdbc.defaultAutoCommit=true

    xml配置

     <context:property-placeholder location="classpath:druid.properties" />
        <!-- druid数据源 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <property name="name" value="${jdbc.name}" />
            <property name="driverClassName" value="${jdbc.driverClassName}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <!-- initialSize:初始化时建立物理连接的个数。默认0,初始化发生在显示调用init方法,或者第一次getConnection时 -->
            <property name="initialSize" value="${jdbc.initialSize}" />
            <!-- maxActive:最大连接池数量,默认8 -->
            <property name="maxActive" value="${jdbc.maxActive}" />
            <!-- minIdle:最小连接池数量 ,默认8 -->
            <property name="minIdle" value="${jdbc.minIdle}" />
            <!-- maxWait:获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
            <property name="maxWait" value="${jdbc.maxWait}" />
            <!-- poolPreparedStatements:是否缓存preparedStatement,也就是PSCache。默认false,PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 -->
            <property name="poolPreparedStatements" value="${jdbc.poolPreparedStatements}" />
            <!-- maxOpenPreparedStatements:要启用PSCache,必须配置大于0,默认-1。当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 -->
            <property name="maxOpenPreparedStatements" value="${jdbc.maxOpenPreparedStatements}" />
            <!-- validationQuery:用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 -->
            <property name="validationQuery" value="${jdbc.validationQuery}" />
            <!-- testOnBorrow:申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认true -->
            <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
            <!-- testOnReturn:归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认false -->
            <property name="testOnReturn" value="${jdbc.testOnReturn}" />
            <!-- testWhileIdle:建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 -->
            <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
            <!-- filters:属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j防御sql注入的filter:wall -->
            <property name="filters" value="${jdbc.filters}" />
            <!-- 这里配置提交方式,默认就是TRUE,可以不用配置 -->
            <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}" />
        </bean>
    
    
        <bean id="studentService" class="com.codedot.db.StudentSv">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
  • 相关阅读:
    Beta冲刺置顶随笔
    Beta总结
    用户试用与调查报告
    Beta冲刺第七天
    Beta冲刺第六天
    Beta冲刺第五天
    Beta冲刺第四天
    Beta冲刺第三天
    Beta冲刺第二天
    爬虫基本操作
  • 原文地址:https://www.cnblogs.com/myitnews/p/13287896.html
Copyright © 2011-2022 走看看