分享

spring

 我是书迷705 2015-04-22

概述

  我印象45年前,我还做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最主要的特点有两个:IoCAOP,这也是J2EE开发企业软件时经常碰到的问题:1)对象太多如何管理;2)共同逻辑和业务逻辑纠缠在一起,错综复杂,如何解耦。

  这篇文章主要关注3个方面:IoCAOP和数据库访问。这里我们假设所有需要的jar都已经准备就绪。

  IoC

  IoC的全称是Inversion of Control,中文称为控制反转, Martin Flower由根据它创造了一个新词:Dependency Injection,中文称为依赖注入。这两个词讲的是一回事儿。

  IoC的实质是如何管理对象,传统意义上我们使用new方式来创建对象,但在企业应用开发的过程中,大量的对象创建都在程序中维护很容易造成资源浪费,并且不利于程序的扩展。

  实现IoC通常有三种方式:

  1)利用接口或者继承,一般以接口较多。这种实现方式和我们平时提到的lazy load有异曲同工之妙。

  2)构造函数注入。

  3)属性注入。

   IoCSpring框架的核心,接下来我们来探索一下SpringIoC的风采。

  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属性来控制对象的声明周期,它包含两个可选值:

  1singleton,表明系统中对于同一个对象,只保留一个实例。

  2prototype,表明系统中每次获取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>

  上面是典型的为属性赋值的示例,其中属性不仅包括简单属性(整数、字符串),也包含了复杂属性(ListMap),还有其他的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,它把AutowiredQualifier组合在一起使用,Qualifier来设置bean的名称,Autowired来设置bean找不到时的行为,requiredtrue时会抛出异常,requiredfalse时会返回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文件中手动定义这个beanSpring会进行自动注册,注册的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框架集合了ProxyFactoryCglib两种方式来实现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>

  测试方法和输出结果同上。

  SpringJDBC

  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中可以看到,这个类的前面有一个名为@TranscationalAnnotation声明,这是Spring实现事务的关键点,它既可以作用在类上,也可以作用在方法上。

  @Transactional包含以下参数:

·        propagation参数,Propagation类型(枚举),默认值为Propogation.REQUIRED,支持的值有REQUIREDMANDATORYNESTEDNEVERNOT_SUPPORTEDREQUIRE_NEWSUPPORTS

·        isolation参数,Isolation类型(枚举),默认值为Isolation.DEFAULT,支持的值有DEFAULTREAD_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READSERIALIZABLE

·        timeout参数,int类型,事务的超时时间,默认值为-1,即不会超时。

·        readOnly参数,boolean类型,true表示事务为只读,默认值为false

·        rollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。

·        rollbackForClassName参数,String[]类型,默认为空数组。

·        noRollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。

·        noRollbackForClassName参数,String[]类型,默认为空数组。

  

 


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多