简单了解一下如何使用Spring框架里JDBC和事务

JDBC概念

Jdbc是一种Java连接数据库技术(Java database connectity), 它是 Java 提供的一些接口,这些接口大部分是数据库厂商提供的(jar包),我们要做的,是连接数据库以后,如何使用Java代码从数据库中存取数据


Spring框架中的JDBC

以前初学时手敲的JDBC即多又麻烦重复,并且涉及事务时需要在filter层进行控制,请求连接多的时候还要使用数据库池连接技术

以上这么多内容Spring框架都已经整合到一起,只需要调用他的一些特定类和配置、注解属性就能直接使用了


JDBC连接方法

确保JDBC需要的Spring的包都已经导入,JDBC的配置文件,还要注意Spring版本和工具的java版本

配置xml文件:数据库连接池有三种,这里用的是C3P0,所以配置时用的是ComboPooledDataSource类。数据库操作对象使用的dbcTemplate,需要依赖注入DataSource。

  1. TransactionTemplate tt = new TransactionTemplate (); // 新建一个TransactionTemplate
  2. Object result = tt . execute (
  3. new TransactionCallback (){
  4. public Object doTransaction ( TransactionStatus status ){
  5. updateOperation ();
  6. return resultOfUpdateOperation ();
  7. }
  8. }); // 执行execute方法进行事务管理

使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。


<?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"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="com.baidu.beans1"></context:component-scan>
	<context:annotation-config/>
	
	<!-- 导入相应包,Spring已经整合好相应的包了 -->
	<!-- 用property-placeholder 加载properties 配置文件 -->
	
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	
	
	<!-- 配置DataSource类 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<!-- ${key} 可以读取properties文件中配置 key对应value -->
		<property name="driverClass" value="${driver}"></property>
		<property name="jdbcUrl" value="${url}"></property>
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
	</bean>
	
	
	<!-- 配置JdbcTemplate类,需要依赖dataSrouce对象 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
</beans>

测试增删查改:

package com.baidu.beans1;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)	//@Test使用JDBC就需要这个
@ContextConfiguration(locations="classpath:beans1.xml")//这个是给@Test用的,加载location里的配置文件
public class Test1 {
	public static void main(String[] args) {
		ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans1.xml");
		JdbcTemplate jdbcTemplate=(JdbcTemplate) applicationContext.getBean("jdbcTemplate");
		List<User> userList = jdbcTemplate.query("select * from user", new RowMapper<User>() {
			public User mapRow(ResultSet rs, int colNum) throws SQLException {
				User user = new User(rs.getInt("uId"),rs.getString("uName"),rs.getString("uSex"));
				return user;
			}
		});
		for (User user : userList) {
			System.out.println(user);
		}
	}
	
	//xml文件里已经配置了JdbcTemplate对象了,这里注解一下
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	@Test
	public void test1() {
	
		List<User> userList = jdbcTemplate.query("select * from user", new RowMapper<User>() {
			public User mapRow(ResultSet rs, int colNum) throws SQLException {
				//User user = new User(rs.getInt("uId"),rs.getString("uName"),rs.getString("uSex"));
				User user=new User(rs.getInt(1), rs.getString(2),rs.getString(3));
				return user;
			}
		});
		for (User user : userList) {
			System.out.println(user);
		}
	}
	
	@Test
	public void test2() {
		String uName = jdbcTemplate.queryForObject("select uName from user where uId=?", String.class,1 );
		System.out.println(uName);
	}
	
	@Test
	public void test3() {
		User user = jdbcTemplate.queryForObject("select * from user where uId=?",new RowMapper<User>() {
			@Override
			public User mapRow(ResultSet rs, int colNum) throws SQLException {
				User user=new User(rs.getInt("uId"),rs.getString("uName"), rs.getString("uSex"));
				return user;
			}
		
		}, 1);
		
		System.out.println(user);
	}
	
	@Test
	public void test4() {
		jdbcTemplate.update("insert into user set uId=?,uName=?,uSex=?",10,"呵呵哒","男");
	}

	
	
	
}

初步理解事务

理解事务之前,先讲一个你日常生活中最常干的事:取钱。 
比如去银行转账时,如果A给B转账100元这个操作可以看做在数据库里更新A的金额数-100和B的金额数+100,但是如果A的金额数-100后程序出现了问题,造成后续的B+100元没有执行,这个时候就要用到事务了。
事务有四个特性:ACID

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

核心接口

Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。 
Spring事务管理涉及的接口的联系如下:

技术分享

实际上是用程序来给数据库发出一些指令,什么时候开启连接什么时候回滚等。


Spring管理接口

latformTransactionManager接口

PlatformTranscationManager是最高级接口了管理着事务,平时主要使用它的实现类DataSourceTransactionManager


管理事务的刷新、回滚等...

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
} 


还有事务的传播方式

Spring不同持久层提供了不同接口,这里使用的是org.springframework.jdbc.datasource.DataSourceTransactionManager



传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

传播行为 含义
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务
简单来说,传播行为解决的问题是事务嵌套问题。

比如说PROPAGATION_REQUIRED,当A方法已经在事务里运行时,又调用了B方法,如果B没有事务,又会重新开启一个事务,在新事务里处理B,如果B有就加入已经存在的事务里

事务隔离级别



转账例子:

xml配置,依赖注入

<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.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd">

	<context:component-scan base-package="com.baidu"></context:component-scan>
	<context:annotation-config/>
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<!-- ${key} 可以读取properties文件中配置 key对应value -->
		<property name="driverClass" value="${driver}"></property>
		<property name="jdbcUrl" value="${url}"></property>
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
	</bean>
	
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<bean id="accountDaoImpl" class="com.baidu.dao.AccountDaoImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<bean id="accountServiceImpl" class="com.baidu.service.AccountServiceImpl">
		<property name="accountDaoImpl" ref="accountDaoImpl"></property><!-- Service在依赖Dao层时,不是注解形式需要set,get? -->
	</bean>
	
	<!-- 配置事务管理层 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!--tx为动态代理类 ,有 上面约束文件有tx才行,这个类已经写好了只管引用就行-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager" >
		<!-- 配置事务的定义信息与事务的状态 -->
		<tx:attributes>
			<!-- 隔离哪些方法,a*表示匹配所有a开头的    隔离级别,是否制度,传播行为 -->
			<tx:method name="transfer" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- AOP切面配置 -->
	<aop:config>
		<!-- 切入点的配置,给哪些方法切 -->
		<aop:pointcut expression="execution(* com.baidu.service..*(..))" id="point1"/>
		<!-- 这个专门用来关联  事务管理和切点的,-->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="point1"/>
		
	</aop:config>
	
</beans>

Dao层

public interface AccountDaoI {
	void outMoney(String name,int money);
	void inMoney(String name,int money);
}
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDaoI{
	
	
	public void outMoney(String name, int money) {
		this.getJdbcTemplate().update("update test set money=money-? where name=?",money,name);
	}

	
	public void inMoney(String name, int money) {
		this.getJdbcTemplate().update("update test set money=money+? where name=?",money,name);
	}

}

Service层

public interface AccountServiceI {
	void transfer();
}
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.baidu.dao.AccountDaoI;

@Transactional	//写表示对所有方法进行事务
public class AccountServiceImpl implements AccountServiceI{
	
	private AccountDaoI accountDaoImpl;
	//如果不是注解需要set,get
	public AccountDaoI getAccountDaoImpl() {
		return accountDaoImpl;
	}

	public void setAccountDaoImpl(AccountDaoI accountDaoImpl) {
		this.accountDaoImpl = accountDaoImpl;
	}
	//也可以单独写
	@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)
	@Override
	public void transfer() {
		accountDaoImpl.outMoney("老王", 10);
		
		accountDaoImpl.inMoney("老李", 10);
	}

}

测试:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.baidu.service.AccountServiceI;
import com.baidu.service.AccountServiceImpl;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans2.xml")
public class Test1 {
	
	@Autowired//这里是接口I,不是实现类
	private AccountServiceI accountServiceImpl;
	
	@Test
	public void test1() {
		accountServiceImpl.transfer();
	}
	
}








猜你喜欢

转载自blog.csdn.net/bibiboyx/article/details/81040354
今日推荐