概述 我印象4、5年前,我还做java开发的时候,Spring是一个非常火的框架,尤其是在Web开发领域,和Struts以及Hibernate构成了SSH三剑客。当时Web开发的另一个组合是LAMP,即Linux+Apache+MySQL+PHP。我在前端方面基本没有实战经验,对js等技术也还是停留在概念和语法方面,所以扬长避短,我对Spring以及Hibernate特别感兴趣。 当年Spring是作为EJB的“替代者”横空出世的,其创始人Rod Johnson还写了一本《J2EE development without EJB》来推行这个框架,这也是一本关于Spring很经典的书,不过最好是在接触Spring一段时间后再去阅读,效果会好一点。 Spring最主要的特点有两个:IoC和AOP,这也是J2EE开发企业软件时经常碰到的问题:1)对象太多如何管理;2)共同逻辑和业务逻辑纠缠在一起,错综复杂,如何解耦。 这篇文章主要关注3个方面:IoC、AOP和数据库访问。这里我们假设所有需要的jar都已经准备就绪。 IoC IoC的全称是Inversion of
Control,中文称为控制反转, Martin Flower由根据它创造了一个新词:Dependency Injection,中文称为依赖注入。这两个词讲的是一回事儿。 IoC的实质是如何管理对象,传统意义上我们使用new方式来创建对象,但在企业应用开发的过程中,大量的对象创建都在程序中维护很容易造成资源浪费,并且不利于程序的扩展。 实现IoC通常有三种方式: 1)利用接口或者继承,一般以接口较多。这种实现方式和我们平时提到的lazy load有异曲同工之妙。 2)构造函数注入。 3)属性注入。 IoC是Spring框架的核心,接下来我们来探索一下Spring中IoC的风采。 IoC简单示例 我们先来定义一个简单的接口和实现: 1 public interface UserDao { 2 void
save(); 3 } 4 5 public class UserDaoImpl implements
UserDao 6 { 7 8 public void save() { 9
System.out.println("save() is called."); 10 } 11 12 } 然后是在classpath下创建一个beans.xml文件(这个文件名不是必须这样的): <?xml version="1.0"
encoding="UTF-8"?> <beans xmlns="http://www./schema/beans" xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:context="http://www./schema/context" xmlns:tx="http://www./schema/tx" xsi:schemaLocation="http://www./schema/beans
http://www./schema/beans/spring-beans-2.5.xsd
http://www./schema/context
http://www./schema/context/spring-context-2.5.xsd
http://www./schema/tx
http://www./schema/tx/spring-tx-2.5.xsd">
<bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl"/> </beans> 接下来是测试代码: 1 private static void test1() 2 { 3
ApplicationContext ctx = new
ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml"); 4
UserDao userDao = (UserDao)ctx.getBean("userDaoImpl"); 5
userDao.save(); 6 } 输出结果如下: save() is called. 我们还可以通过工厂方式来创建对象。 通过静态工厂创建Bean 添加一个类,如下: 1 public class UserDaoFactory { 2 3 public static UserDao getUserDao() 4 { 5
return new UserDaoImpl(); 6 } 7 } 在beans.xml中,添加如下内容: 1 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoFactory" factory-method = "getUserDao"/> 测试代码和执行结果和上面类似,不再赘述。 通过实例工厂创建Bean 添加如下类: 1 public class UserDaoFactory2 2 { 3 public UserDao getUserDao() 4 { 5
return new UserDaoImpl(); 6 } 7 } 这个类和UserDaoFactory唯一的区别是这里的getUserDao是实例方法,而不是静态方法。 在beans.xml中追加如下内容: 1 <bean id="factory" class="sample.spring.ioc.UserDaoFactory2"/> 2 <bean id="userDaoImpl3" factory-bean="factory" factory-method="getUserDao"/> 测试方法和结果同上。 对象的生命周期 我们可以通过设置bean节点的scope属性来控制对象的声明周期,它包含两个可选值: 1)singleton,表明系统中对于同一个对象,只保留一个实例。 2)prototype,表明系统中每次获取bean时,都新建一个对象。 我们修改beans.xml文件: 1 <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl" scope="singleton"/> 2 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoImpl" scope="prototype"/> 这两个bean指向同一个类型,但是scope的设置不同。 下面是测试方法: 1 private static void scopeTest() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/scope.xml"); 4
System.out.println("=====Singleton test====="); 5 UserDao userDao1A =
(UserDao)ctx.getBean("userDaoImpl"); 6 UserDao userDao1B =
(UserDao)ctx.getBean("userDaoImpl"); 7 System.out.println("userDao1A
== userDao1B:" + (userDao1A==userDao1B)); 8
System.out.println("=====Prototype test====="); 9 UserDao userDao2A =
(UserDao)ctx.getBean("userDaoImpl2"); 10
UserDao userDao2B = (UserDao)ctx.getBean("userDaoImpl2"); 11
System.out.println("userDao2A == userDao2B:" +
(userDao2A==userDao2B)); 12 } 执行结果如下: =====Singleton
test===== userDao1A ==
userDao1B:true =====Prototype
test===== userDao2A ==
userDao2B:false 如何设置对象属性 上面的示例中,我们的对象中没有包含属性,对于业务对象来说,这一般是不现实。实际中的对象或多或少都会有一些属性。 Spring支持两种方式对属性赋值:set方式和构造函数。 下面我们会分别描述两种方式,但首先我们需要展示业务对象: 1 public class UserServiceBean 2 { 3 private int userID; 4 private
String userName; 5 private
UserDao userDao; 6 private
List<String> hobbies; 7 private
Map<String, Integer> scores; 8 9 public
UserServiceBean(int userID, String userName, UserDao userDao,
List hobbies,Map scores) 10 { 11
this.userID = userID; 12
this.userName = userName; 13
this.userDao = userDao; 14
this.hobbies = hobbies; 15
this.scores = scores; 16 } 17 18 public UserServiceBean(){} 19 20 public void setUserID(int
userID) { 21
this.userID = userID; 22 } 23 public int getUserID() { 24
return userID; 25 } 26 public void setUserName(String userName) { 27
this.userName = userName; 28 } 29 public String getUserName() { 30
return userName; 31 } 32 public void setUserDao(UserDao userDao) { 33
this.userDao = userDao; 34 } 35 public UserDao getUserDao() { 36
return userDao; 37 } 38 public void setHobbies(List<String> hobbies) { 39
this.hobbies = hobbies; 40 } 41 public List<String> getHobbies() { 42
return hobbies; 43 } 44 public void setScores(Map<String, Integer> scores)
{ 45
this.scores = scores; 46 } 47 public Map<String, Integer> getScores() { 48
return scores; 49 } 50 } 这是一个典型的学生信息,包括学号、姓名、爱好和成绩。 通过Set方式为对象属性赋值 我们在beans.xml中追加如内容: 1 <bean id="userService" class="sample.spring.ioc.UserServiceBean"> 2 <property
name="userID" value="1"/> 3 <property
name="userName" value="张三"/> 4 <property
name="userDao" ref="userDaoImpl"/> 5 <property
name="hobbies"> 6 <list> 7 <value>羽毛球</value> 8 <value>看电影</value> 9 <value>弹吉他</value> 10
</list> 11 </property> 12 <property name="scores"> 13
<map> 14
<entry key="数据结构" value="90"/> 15
<entry key="编译原理" value="85"/> 16
<entry key="离散数学" value="82"/> 17
</map> 18 </property> 19 </bean> 上面是典型的为属性赋值的示例,其中属性不仅包括简单属性(整数、字符串),也包含了复杂属性(List、Map),还有其他的bean。 下面是测试代码: 1 private static void propertyTest1() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml"); 4 UserServiceBean userService =
(UserServiceBean)ctx.getBean("userService"); 5 printUserService(userService); 6 } 7 8 private static void printUserService(UserServiceBean
userService) 9 { 10
System.out.println("编号:" + userService.getUserID()); 11
System.out.println("姓名:" + userService.getUserName()); 12
System.out.println("爱好:"); 13 for(String hobby:userService.getHobbies()) 14 { 15
System.out.println(hobby); 16 } 17
System.out.println("学习成绩:"); 18 for(Entry<String,Integer> entry:userService.getScores().entrySet()) 19 { 20
System.out.println(entry.getKey() + "\t" + entry.getValue()); 21 } 22
userService.getUserDao().save(); 23 } 输出结果如下: 编号:1 姓名:张三 爱好: 羽毛球 看电影 弹吉他 学习成绩: 数据结构 90 编译原理 85 离散数学 82 save() is called. 通过构造函数为对象属性赋值 我们也可以通过构造函数来为对象赋值,在上面定义UserServiceBean时,我们已经添加了一个构造函数。下面来看beans.xml中的配置: 1 <bean id="userService2" class="sample.spring.ioc.UserServiceBean"> 2 <constructor-arg
index="0"
value="1"/> 3 <constructor-arg
index="1"
value="张三"/> 4 <constructor-arg
index="2"
ref="userDaoImpl"/> 5 <constructor-arg
index="3"> 6 <list> 7 <value>羽毛球</value> 8 <value>看电影</value> 9 <value>弹吉他</value> 10
</list> 11 </constructor-arg> 12 <constructor-arg index="4"> 13
<map> 14
<entry key="数据结构" value="90"/> 15
<entry key="编译原理" value="85"/> 16
<entry key="离散数学" value="82"/> 17
</map> 18 </constructor-arg> 19 </bean> 测试代码和输出结果同上。 需要注意:我们定义的业务对象应该保留默认的构造函数。 使用Annotation来定位Bean 在Spring中,除了在xml配置文件中定义对象,我们还可以使用Annotation来定位,这位我们提供了很大的方便。 这里我们使用的Annotation主要包括:@Resource/@Autowried/@Qualifier。 来看下面的示例: 1 public class UserServiceBean2 2 { 3 private
String userID; 4 private
String userName; 5
@Resource(name="userDaoImpl") 6 private
UserDao userDao1; 7 private
UserDao userDao2; 8 9 @Autowired(required=false) 10
@Qualifier("userDaoImpl") 11 private UserDao userDao3; 12 13
@Autowired(required=false) 14
@Qualifier("userDaoImpl3") 15 private UserDao userDao4; 16 17 public void setUserID(String userID) { 18
this.userID = userID; 19 } 20 public String getUserID() { 21 return userID; 22 } 23 public void setUserName(String userName) { 24
this.userName = userName; 25 } 26 public String getUserName() { 27
return userName; 28 } 29
@Resource 30 public void setUserDao2(UserDao userDao2) { 31
this.userDao2 = userDao2; 32 } 33 public UserDao getUserDao2() { 34
return userDao2; 35 } 36 37 public void test() 38 { 39
userDao1.save(); 40
userDao2.save(); 41
System.out.println(userDao3.getClass().getName()); 42
userDao3.save(); 43 } 44 } 测试方法: 1 private static void annotationTest() 2 { 3
ApplicationContext ctx = new
ClassPathXmlApplicationContext("sample/spring/ioc/annotation.xml"); 4
UserServiceBean2 userService =
(UserServiceBean2)ctx.getBean("userService"); 5 6
userService.test(); 7 } 输出结果如下: save() is called. save() is called. sample.spring.ioc.UserDaoImpl save() is called. 我们来对上面示例中出现的Annotation来进行说明。 1 @Resource(name="userDaoImpl") 2 private UserDao userDao1; 这是定义在字段上的Annotation,是指userDao1使用xml配置文件中定义的名为“userDaoImpl”的bean进行填充。 1 @Autowired(required=false) 2 @Qualifier("userDaoImpl") 3 private UserDao userDao3; 这是第二种类型的Annotation,它把Autowired和Qualifier组合在一起使用,Qualifier来设置bean的名称,Autowired来设置bean找不到时的行为,required为true时会抛出异常,required为false时会返回null。 1 @Resource 2 public void setUserDao2(UserDao userDao2) { 3 this.userDao2 = userDao2; 4 } 这是作用在setter上的Annotation,@Resource 可以不写明name参数,这时Spring会首先按照名字然后按照数据类型的方式去定位bean。 自动加载对象定义 对于大型系统来说,我们可能会创建大量的类,如果这些类的声明都需要写在xml文件里的话,会产生额外大量的工作。 Spring提供了一种简单的机制让我们的对象可以自动注册。 我们可以在beans.xml中添加如下内容: 1 <context:component-scan base-package="sample.spring.ioc"/> 然后我们可以在sample.spring.ioc包下的对象,添加@Component/@Service/@Controller/@repository,这样Spring会自动将带有这些Annotation的类进行注册。 下面是一个示例: 1 @Service("userService") 2 public class UserServiceBean3 3 { 4 private
String userID; 5 private
String userName; 6
@Resource(name="userDaoImpl") 7 private
UserDao userDao1; 8 9 @Autowired(required=true) 10
@Qualifier("userDaoImpl") 11 private UserDao userDao3; 12 13 public void setUserID(String userID) { 14
this.userID = userID; 15 } 16 public String getUserID() { 17
return userID; 18 } 19 public void setUserName(String userName) { 20
this.userName = userName; 21 } 22 public String getUserName() { 23
return userName; 24 } 25 // @Resource 26 // public void
setUserDao2(UserDao userDao2) { 27 // this.userDao2 = userDao2; 28 // } 29 // public UserDao getUserDao2() { 30 // return userDao2; 31 // } 32 33 public void test() 34 { 35
userDao1.save(); 36 // userDao2.save(); 37
System.out.println(userDao3.getClass().getName()); 38
userDao3.save(); 39 } 40 } 这个类和上面定义的UserServiceBean2非常相似,需要注意在类前面添加的Annotation信息。 我们不需要在xml文件中手动定义这个bean,Spring会进行自动注册,注册的bean名称是userService。 AOP 我们在Java回顾之反射中已经设计了一个简单的AOP框架,通常情况下,对于AOP,我们有两种方式来实现。 使用DynamicProxy实现AOP 下面是一个简单的示例,首先定义业务对象: 1 public interface UserDao { 2 3 void
save(); 4 } 5 6 public class UserDaoImpl implements
UserDao 7 { 8 private
String name; 9 10 public void save() { 11
System.out.println("save() is called for " + name); 12 } 13 14 public void setName(String name) { 15
this.name = name; 16 } 17 18 public String getName() { 19
return name; 20 } 21 } 下面是一个实现了InvocationHandler的类: 1 public class ProxyFactory implements
InvocationHandler 2 { 3 private
Object target; 4 5 public
Object createUserDao(Object target) 6 { 7 this.target
= target; 8 return
Proxy.newProxyInstance(this.target.getClass().getClassLoader(), 9 this.target.getClass().getInterfaces(), this); 10 } 11 12 public Object invoke(Object proxy, Method method, Object[]
args) 13
throws Throwable { 14
15
UserDaoImpl userDao = (UserDaoImpl)target; 16
Object result = null; 17
if(userDao.getName() != null) 18
{ 19
result = method.invoke(target, args); 20
} 21
else 22
{ 23
System.out.println("The name is null."); 24
} 25
return result; 26 } 27 } 接下来是测试代码: 1 private static void test1() 2 { 3
ProxyFactory pf = new ProxyFactory(); 4
UserDao userDao = (UserDao)pf.createUserDao(new
UserDaoImpl()); 5
userDao.save(); 6 } 执行结果如下: The name is null. 这是因为userDao的类型是UserDao,它是一个接口,并没有定义name字段,因此name=null。 使用Cglib实现AOP 同样是上面的需求,我们假设并没有继承的接口,这我们可以使用cglib来实现。 首先我们重新定义一个UserDaoImpl2,它不会实现任何接口: 1 public class UserDaoImpl2 2 { 3 private
String name; 4 5 public void save() throws InterruptedException { 6 Thread.sleep(3000); 7
System.out.println("save() is called for " + name); 8 } 9 10 public void setName(String name) { 11
this.name = name; 12 } 13 14 public String getName() { 15
return name; 16 } 17 18 public void raiseException() 19 { 20
throw new RuntimeException("This is test."); 21 } 22 } 然后是创建CglibFactory: 1 public class CglibFactory implements
MethodInterceptor 2 { 3 private
Object target; 4 public
Object createUserDao(Object target) 5 { 6 this.target
= target; 7 Enhancer enhancer = new Enhancer(); 8
enhancer.setSuperclass(target.getClass()); 9 enhancer.setCallback(this); 10
return enhancer.create(); 11 } 12 13 public Object intercept(Object proxy, Method method, Object[]
args, 14
MethodProxy methodProxy) throws Throwable { 15
UserDaoImpl2 userDao = (UserDaoImpl2)target; 16
if (userDao.getName() != null) 17
{ 18
return method.invoke(target, args); 19
} 20
else 21
{ 22
System.out.println("The name is null."); 23
} 24
return null; 25 } 26 } 它实现了MethodInterceptor接口,其中包括intercept方法,这个方法就会通过反射的方式来触发目标方法,同时还可以添加一些其他处理。 下面是测试方法: 1 private static void test2() throws
InterruptedException 2 { 3 CglibFactory cf = new CglibFactory(); 4 UserDaoImpl2 temp = new UserDaoImpl2(); 5 UserDaoImpl2 userDao =
(UserDaoImpl2)cf.createUserDao(temp); 6 userDao.save(); 7 temp.setName("Zhang
San"); 8 userDao =
(UserDaoImpl2)cf.createUserDao(temp); 9 userDao.save(); 10 } 输出结果如下: The name is null. save() is called for Zhang San 使用Spring实现AOP Spring框架集合了ProxyFactory和Cglib两种方式来实现AOP。 我们来看一个示例,还是使用上面定义的UserDaoImpl以及UserDaoImpl2。 首先需要定义一个interceptor: 1 @Aspect 2 public class MyInterceptor { 3 4 @Pointcut("execution (*
sample.spring.aop.*.*(..))") 5 public void anyMethod(){} 6 7
@Before("anyMethod()") 8 public void before() 9 { 10
System.out.println("Before"); 11 } 12 13
@After("anyMethod()") 14 public void after() 15 { 16
System.out.println("After"); 17 } 18 19
@Around("anyMethod()") 20 public void Around(ProceedingJoinPoint pjp) throws Throwable 21 { 22
long start = System.currentTimeMillis(); 23
pjp.proceed(); 24
long end = System.currentTimeMillis(); 25
System.out.println("执行时间:" + (end - start)); 26 } 27 28
@Before("anyMethod() && args(name)") 29 public void before(String name) 30 { 31
System.out.println("The name is " + name); 32 } 33 34
@AfterReturning(pointcut="anyMethod()",
returning="result") 35 public void afterReturning(String result) 36 { 37 System.out.println("The value is
" + result); 38 } 39 40
@AfterThrowing(pointcut="anyMethod()", throwing="e") 41 public void afterThrowing(Exception e) 42 { 43
e.printStackTrace(); 44 } 45 } 我们可以看到上面的代码中包含了一些Annotation,这些Annotation是用来实现AOP的关键。 然后需要修改beans.xml,添加如下内容: 1 <aop:aspectj-autoproxy /> 2 <bean id="userDaoImpl" class = "sample.spring.aop.UserDaoImpl"/> 3 <bean id="userDaoImpl2" class = "sample.spring.aop.UserDaoImpl2"/> 4 <bean id="myInterceptor" class="sample.spring.aop.MyInterceptor"/> 其中第一行是让Spring打开AOP的功能,下面三行定义了三个bean,这里我们把interceptor也看做是一个bean对象。 接下来是测试代码: 1 private static void test3() throws
InterruptedException 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/aop/beans.xml"); 4 UserDao userDao =
(UserDao)ctx.getBean("userDaoImpl"); 5 userDao.save(); 6 UserDaoImpl2 userDao2 =
(UserDaoImpl2)ctx.getBean("userDaoImpl2"); 7 userDao2.save(); 8 userDao2.setName("Zhang
San"); 9 String name =
userDao2.getName(); 10 // userDao2.raiseException(); 11 } 这里我们可以看到,测试方法中既使用了UserDaoImpl1(这里是UserDao接口),也是用了UserDaoImpl2。正如我们上面所言,在Spring中,如果类实现了接口,Spring会按照ProxyFactory的方式来处理;如果没有实现接口,Spring会按照Cglib的方式来处理。 上面测试方法的输出如下: Before Before save() is called for null 执行时间:1 The value is null After After 执行时间:1 The value is null Before Before save() is called for null 执行时间:3001 The value is null After After 执行时间:3002 The value is null Before The name is Zhang
San Before 执行时间:26 The value is null After After 执行时间:27 The value is null Before Before 执行时间:0 The value is null After After 执行时间:1 The value is null 使用Spring配置文件来配置AOP 上面的示例中,我们使用Annotation来配置AOP的信息,同样我们也可以使用xml文件的方式来配置AOP。 还是以上面定义的interceptor为基础,我们去掉里面所有的Annotation,然后在beans.xml中添加如下内容: 1 <bean id="myInterceptor2" class="sample.spring.aop.MyInterceptor2"/> 2 <aop:config> 3 <aop:aspect
id="asp" ref="myInterceptor2"> 4 <aop:pointcut
id="anyMethod" expression="execution (* sample.spring.aop.*.*(..))"/> 5 <aop:before
pointcut-ref="anyMethod" method="before"/> 6 <aop:after
pointcut-ref="anyMethod" method="after"/> 7 <aop:around
pointcut-ref="anyMethod" method="around"/> 8 <aop:after-returning
pointcut-ref="anyMethod" method="afterReturning" returning="result"/> 9 <aop:after-throwing
pointcut-ref="anyMethod" method="afterThrowing" throwing="e"/> 10 </aop:aspect> 11 </aop:config> 测试方法和输出结果同上。 Spring和JDBC Spring中也包含了对JDBC数据访问的支持,它有一个JdbcTemplate的机制,其中提供了大量的API,对ResultSet进行了封装,可以大大简化我们的工作量。 同时Spring还提供了针对事务的支持,包含了一些Annotation,既可以作用在类上,也可以作用在方法上。 下面是一个简单的示例,我们还是连接MySQL数据库中的user表,实现对其CRUD操作。 首先是定义业务对象以及DAO接口: 1 public class User { 2 3 private int userID; 4 private
String userName; 5 public void setUserID(int userID) { 6 this.userID
= userID; 7 } 8 public int getUserID() { 9 return
userID; 10 } 11 public void setUserName(String userName) { 12
this.userName = userName; 13 } 14 public String getUserName() { 15
return userName; 16 } 17 } 18 19 public interface UserDao { 20 21 void insertUser(User user); 22 void updateUser(User user); 23 void deleteUser(User user); 24
List<User> getAllUser(); 25
User getUser(int id); 26 } 然后是建立一个Dao的实现类,这里使用了一些JdbcTemplate API: 1 @Transactional 2 public class UserDaoImpl implements
UserDao 3 { 4 private
JdbcTemplate jdbcTemplate; 5 6 public void setDataSource(DataSource dataSource) throws
SQLException 7 { 8 jdbcTemplate = new JdbcTemplate(dataSource); 9
System.out.println(dataSource.getConnection().getMetaData().getDriverName()); 10 } 11 12 public void deleteUser(User user) { 13
jdbcTemplate.update("delete from user where id=?", 14 new
Object[]{user.getUserID()}, 15 new int[]{java.sql.Types.INTEGER}); 16 } 17 18
@SuppressWarnings("unchecked") 19 public List<User> getAllUser() { 20
return
(List<User>)jdbcTemplate.query("select * from user", 21 new
RowMapper() 22 { 23 public Object mapRow(ResultSet rs, int arg) throws SQLException 24
{ 25 User user = new User(); 26
user.setUserID(rs.getInt("ID")); 27
user.setUserName(rs.getString("NAME")); 28 29 return user; 30 } 31 }); 32 } 33 34 public User getUser(int id) { 35
try 36
{ 37
return
(User)jdbcTemplate.queryForObject("select * from user where id=?", 38 new
Object[]{id}, 39 new int[]{java.sql.Types.INTEGER}, 40 new
RowMapper() 41 { 42 public Object mapRow(ResultSet rs, int arg) throws SQLException 43 { 44 User user = new User(); 45
user.setUserID(rs.getInt("id")); 46
user.setUserName(rs.getString("name")); 47 return user; 48 } 49 }); 50
} 51
catch(Exception ex) 52
{ 53
System.out.println(ex.getMessage()); 54
} 55
return null; 56
57 } 58 59 public void insertUser(User user) { 60
jdbcTemplate.update("insert into user (id,name) values(?,?)", 61 new
Object[]{user.getUserID(), user.getUserName()}, 62 new int[]{java.sql.Types.INTEGER, java.sql.Types.VARCHAR}); 63 } 64 65 public void updateUser(User user) { 66
jdbcTemplate.update("update user set name=? where id=?", 67 new
Object[]{user.getUserName(), user.getUserID()}, 68 new int[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER}); 69 } 70 71 } JdbcTemplate还提供了一些其他的API,也非常实用。 接下来需要修改beans.xml: 1 <bean id="theDatasource" class="org.apache.commons.dbcp.BasicDataSource"> 2 <property
name="driverClassName" value="com.mysql.jdbc.Driver" /> 3 <property
name="url"
value="jdbc:mysql://localhost/test" /> 4 <property
name="username" value="root" /> 5 <property
name="password" value="123" /> 6 <property
name="initialSize" value="2" /> 7 <property
name="maxActive" value="100" /> 8 <property
name="maxIdle" value="2" /> 9 <property
name="minIdle" value="1" /> 10 </bean> 11 12 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 13 <property name="dataSource" ref="theDatasource" /> 14 </bean> 15 16 <tx:annotation-driven transaction-manager="txManager" /> 17 18 <bean id="userDao" class="sample.spring.jdbc.UserDaoImpl"> 19 <property name="dataSource" ref="theDatasource"/> 20 </bean> 这里theDataSource用来配置数据库连接信息;txManager来配置事务管理器的信息;userDao是我们的Dao实现类信息。 下面是测试方法: 1 public static void test1() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/jdbc/beans.xml"); 4 UserDao userDao =
(UserDao)ctx.getBean("userDao"); 5
System.out.println("=====get all user====="); 6 List<User> users =
userDao.getAllUser(); 7 for(User
user:users) 8 { 9
System.out.println("ID:" + user.getUserID() +
";Name:" + user.getUserName()); 10 } 11
System.out.println("=====insert user====="); 12
User user = new User(); 13
user.setUserID(10); 14
user.setUserName("Zhang Fei"); 15
userDao.insertUser(user); 16
user = userDao.getUser(10); 17 System.out.println("ID:"
+ user.getUserID() + ";Name:" + user.getUserName()); 18
System.out.println("=====update user====="); 19
user.setUserName("Devil"); 20
userDao.updateUser(user); 21
user = userDao.getUser(10); 22
System.out.println("ID:" + user.getUserID() +
";Name:" + user.getUserName()); 23
System.out.println("=====delete user====="); 24
userDao.deleteUser(user); 25
user = userDao.getUser(10); 26 if (user == null) 27 { 28
System.out.println("delete successfully."); 29 } 30 } 输出结果如下: MySQL-AB JDBC
Driver =====get all
user===== ID:1;Name:Zhang
San ID:2;Name:TEST =====insert
user===== ID:10;Name:Zhang
Fei =====update
user===== ID:10;Name:Devil =====delete
user===== Incorrect result
size: expected 1, actual 0 delete
successfully. 说到数据库事务,我们在上面的UserDaoImpl中可以看到,这个类的前面有一个名为@Transcational的Annotation声明,这是Spring实现事务的关键点,它既可以作用在类上,也可以作用在方法上。 @Transactional包含以下参数: ·
propagation参数,Propagation类型(枚举),默认值为Propogation.REQUIRED,支持的值有REQUIRED、MANDATORY、NESTED、NEVER、NOT_SUPPORTED、REQUIRE_NEW、SUPPORTS。 ·
isolation参数,Isolation类型(枚举),默认值为Isolation.DEFAULT,支持的值有DEFAULT、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE。 ·
timeout参数,int类型,事务的超时时间,默认值为-1,即不会超时。 ·
readOnly参数,boolean类型,true表示事务为只读,默认值为false。 ·
rollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。 ·
rollbackForClassName参数,String[]类型,默认为空数组。 ·
noRollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。 ·
noRollbackForClassName参数,String[]类型,默认为空数组。
|
|