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>
  • 相关阅读:
    【Leetcode】23. Merge k Sorted Lists
    【Leetcode】109. Convert Sorted List to Binary Search Tree
    【Leetcode】142.Linked List Cycle II
    【Leetcode】143. Reorder List
    【Leetcode】147. Insertion Sort List
    【Leetcode】86. Partition List
    jenkins 配置安全邮件
    python 发送安全邮件
    phpstorm 同步远程服务器代码
    phpUnit 断言
  • 原文地址:https://www.cnblogs.com/myitnews/p/13287896.html
Copyright © 2011-2022 走看看