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。
-
TransactionTemplate tt = new TransactionTemplate (); // 新建一个TransactionTemplate
-
Object result = tt . execute (
-
new TransactionCallback (){
-
public Object doTransaction ( TransactionStatus status ){
-
updateOperation ();
-
return resultOfUpdateOperation ();
-
}
-
}); // 执行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();
}
}