Spring+JDBC的简单配置和开发

  • Post author:
  • Post category:其他


目前很多的公司采用了spring+jdbc的配置开发项目,下面介绍怎么配置环境到开发时候的注意事项:

①引入必要的jar文件

JDBC驱动(mysql为例):

mysql-connector-5.1.7.jar

数据库连接池(dbcp为例):

commons-dbcp.jar

commons-pool.jar

spring核心必须包:

spring.jar

commons-logging.jar

AOP非必须(方便使用建议添加):

cglib-nodep-2.1_3.jar

aspectjrt.jar

注解(一般都会使用):

common-annotations.jar

aspectjweaver.jar

这里写图片描述

一、配置数据源dataSource:

学过了spring后,这里采用spring容器管理来实例化数据源,一般在配置之前都会开启事务管理的命名空间,一般都会使用事务管理。

在配置一个工程的时候一定要注意的是循序渐进,不要一口气全部配置好所有的东西,这样如果经验不丰富的朋友,可能难以解决现有的报错。所以在配置好数据源的时候测试一下这个数据源是否正确,如果经验比较丰富的可以多配置几步再测试。

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

        <!-- 开启注解 -->
        <context:annotation-config/>

        <!-- 配置数据连接池 -->
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="url" value="jdbc:mysql:///test?useUnicode=true&amp;characterEncoding=UTF-8"></property>
            <property name="driverClassName" value="org.gjt.mm.mysql.Driver"></property>
            <property name="username" value="root"></property>
            <property name="password" value="heyingxxx"></property>

            <!-- 连接池初始值 -->
            <property name="initialSize" value="2"/>
            <!-- 连接池最大值 -->
            <property name="maxActive" value="100"/>
            <!-- 连接池最大空闲值 -->
            <property name="maxIdle" value="5"/>
            <!-- 连接池最小空闲值 -->
            <property name="minIdle" value="2"/>
        </bean>
</beans>

这个dataSource已经交给spring容器管理了,现在使用spring实例化这个bean,以前介绍过方法:

public void testSave() throws Exception {
        try {
            AbstractApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//          PersonService personService = (PersonService) context.getBean("personService");
            DataSource dataSource = (DataSource) context.getBean("dataSource");
//          personService.save(new Person("heying", 2));
            System.out.println(dataSource.getConnection());
            context.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里写图片描述

二、配置业务bean,这边模拟一个员工的录入,简化操作,user只有两个属性,id和name


Person.java:

package com.heying.bean;

public class Person {
    private String name;
    private Integer id;

    // 初始化person
    public Person(String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
}


PersonService接口:

package com.heying.service;

import java.util.List;

import com.heying.bean.Person;
public interface PersonService {
    public void save(Person person);
    public void update(Integer id,Person person);
    public List<Person> findAll();
}


PersonService接口的实现类:


业务层面需要调用到数据库,所以需要将dataSource注入到PersonServiceBean,在dataSource无法直接实现数据的增删改查,如果这里只是单单的把数据库注入到PersonServiceBean显得有些不足,需要通过getConnection等大量的重复动作, 所以在JdbcTemplate中有很多现有的方法,这样就需要在使用PersonServiceBean的时候不仅需要注入dataSource,还需要初始化JdbcTemplate,这边可以使用两个或者多个方式。第一种是前面介绍的在业务bean->PersonServiceBean初始化时候注入dataSource时候采用setter方式,在setter中new JdbcTemplate(dataSource),还有一种就是在实例化业务bean->PersonServiceBean的时候使用init方法,值得注意的是,一定在注入了dataSource之后初始化JdbcTemplate,否则dataSource为null将报错,因为JdbcTemplate需要数据源初始化。这边采用init初始化:

package com.heying.service.impl;

import java.util.List;
import javax.annotation.Resource;
import javax.sql.DataSource;

import com.heying.bean.Person;
import com.heying.service.PersonService;

    public class PersonServiceBean implements PersonService{
        @Resource
        private DataSource dataSource; //注入dataSource到PersonServiceBean
        private JdbcTemplate jdbcTemplate ; // 可以使用init方法初始化,也可以在bean.xml中配置使用setter方法注入PersonServiceBean时候初始化

        public void init(){
        jdbcTemplate = new JdbcTemplate(dataSource);
        }

        @Override
        public void save(Person person) {
            try {
                System.out.println(dataSource.getConnection());
                System.out.println(jdbcTemplate);
                jdbcTemplate.update("insert into t_user (name,id) values (?,?) ", 
                    new Object[]{person.getName(),person.getId()},
                    new int[]{Types.VARCHAR,Types.INTEGER});
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void update(Integer id, Person person) {

        }

        @Override
        public List<Person> findAll() {
            return null;
        }
}


bean.xml文件:

<bean id="personService" class="com.heying.service.impl.PersonServiceBean" init-method="init"></bean>


测试:

@Test
    public void testSave() throws Exception {
        try {
            AbstractApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            PersonService personService = (PersonService) context.getBean("personService");
            personService.save(new Person("heying51123", 1102));
            context.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


结果:

这里写图片描述


数据库:

这里写图片描述


基于注解的事务管理器:

这样就能完成了一个完整的过程,但是在企业开发中还必须使用事务管理,所以,还必须配置事务管理,防止异常发生时同一个事务中所有的操作回滚,这里注意的是在spring中的事务管理,默认只回滚RuntimeException()的异常,这类异常不能通过捕获处理,所以就不需要捕获的异常是特殊的Exception的子类(unchecked Exception),而其他的异常也是Exception的子类,这些属于检查异常,默认是不回滚事务的,所以需要在回滚的方法上定义   @Transactional(rollbackFor=Exception.class):


bean.xml文件:


事务管理的命名空间已经引入,要想使事务交给spring管理,需要和注解一样需要注册事务管理器,初始化时就需要将dataSource注入到txManager,交给事务管理器管理

        <!-- 注册事务管理器 -->
        <tx:annotation-driven transaction-manager="txManager"/>

        <!-- 配置事务管理器 -->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>


第一次使用insert插入重复主键看看数据库是否回滚:


save方法:

    @Override
    public void save(Person person) throws Exception{
        try {
            System.out.println(dataSource.getConnection());
            System.out.println(jdbcTemplate);
            int updateRows = jdbcTemplate.update("insert into t_user (name,id) values (?,?) ", 
                    new Object[]{person.getName(),person.getId()},
                    new int[]{Types.VARCHAR,Types.INTEGER});
            int updateRows2 = jdbcTemplate.update("insert into t_user (name,id) values (?,?) ", 
                    new Object[]{person.getName(),person.getId()},
                    new int[]{Types.VARCHAR,Types.INTEGER});
            System.out.println("insert影响了: "+updateRows+ " ROWS");
        } catch (Exception e) {
            throw new Exception("普通捕获异常");
        }
    }


测试:

    public class TestCase {
    private static AbstractApplicationContext context;
    private static PersonService personService;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        context = new ClassPathXmlApplicationContext("bean.xml");
        personService = (PersonService) context.getBean("personService");
    }

    @Test
    public void testSave() throws Exception {
        try {
            personService.save(new Person("heying110", 1102));
        } catch (Exception e) {
            System.err.println("********异常********* "+e.getMessage()); 
        }finally{
            List<Person> list = personService.findAll();
            for (int i = 0; i < list.size(); i++) {
                System.out.println("##-->> "+list.get(i).getName());
                System.out.println("##-->> "+list.get(i).getId());
            }
            context.close();
        }
    }
}


结果:


这里写图片描述


总结:

显然第二条数据检查异常并没有影响第一条数据的插入


第二次加上@Transactional(rollbackFor=Exception.class)定义需要回滚的异常:

    @Transactional(rollbackFor=Exception.class)
    public void save(Person person) {
        fun()...
    }


测试:

personService.save(new Person("heying110", 1103));


结果:

这里写图片描述

1103没有插入,第二条记录出现异常,第一条就回滚了,一般数据库访问出现的异常比较基层,一般不可能通过try语句快解决的,这样也有的喜欢抛出一个运行期异常,不用指明@Transactional(rollbackFor=Exception.class)也能回滚所有出现异常的操作。

不是所有的访问都需要事务的,比如查询操作可以不适用事务管理,使用事务管理,必定会影响性能,所以介绍几种常见的事务传播属性(Propagation )的方式:


REQUIRES

(默认):加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务


PROPAGATION_SUPPORTS

:支持当前事务,如果当前没有事务,就以非事务方式执行,跟随调用的方法是否有事务决定。


PROPAGATION_MANDATORY

:要求在一个已有的事务中执行,业务方法不能发起自己的事务,如果当前没有事务,就抛出异常。


PROPAGATION_REQUIRES_NEW

:不管业务没有没有事务,总会开启新事务,如果调用方法中存在事务,把当前事务挂起,新的事务会创建,直到方法结束后,新事务才算结束,原先挂起的事务恢复执行。


PROPAGATION_NOT_SUPPORTED

:以非事务方式执行操作,如果被另外一个方法调用,且当前存在事务,就把当前事务挂起,调用方法结束后,原先事务在继续执行。


PROPAGATION_NEVER

:要求在一个没有事务方式中执行,如果存在事务,则抛出异常,和MANDATORY相反。


NESTED

:如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动的事务,则按照REQUIRES属性执行,它使用一个单独的事务,这个事务拥有多个回滚保存点,内部事务不会对外部事务产生影响,它只对

DataSourceTransactionManager

事务管理起效。


Spring事务隔离级别:


1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

另外四个与JDBC的隔离级别相对应

2. ISOLATION_READ_UNCOMMITTED(读未提交): 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻像读。

3. ISOLATION_READ_COMMITTED:(读已提交) 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据

4. ISOLATION_REPEATABLE_READ(可重复读): 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读 ,它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生。

5. ISOLATION_SERIALIZABLE:(串行化) 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行, 除了防止脏读,不可重复读外,还避免了幻像读。

mysql数据隔离级别藏参考:


http://xm-king.iteye.com/blog/770721

介绍一下基于XML方式的事务管理,采用了AOP中的通知管理将事务交给spring管理

        <aop:config>
            <!-- 定义一个切面,拦截的包以及子包类任意类任意方法 -->
            <aop:pointcut id="transactionPointcut" expression="execution(* com.heying.service.PersonService..*.*(..))"/>
            <aop:advisor advice-ref="txtAdvice" pointcut-ref="transactionPointcut"/>
        </aop:config>
        <tx:advice id="txtAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/> <!-- get开头方法不使用 -->
                <tx:method name="*"/><!-- 其他方法,默认 propagation="REQUIRED" -->                
            </tx:attributes>
        </tx:advice>



版权声明:本文为u011249702原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。