• Java回顾之Spring基础


    这一篇主要讲Spring一些基础的内容。

      概述

           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.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            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/tx http://www.springframework.org/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方式和构造函数。

      下面我们会分别描述两种方式,但首先我们需要展示业务对象:

    定义UserServiceBean

      这是一个典型的学生信息,包括学号、姓名、爱好和成绩。

      通过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() + "	" + 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的类进行注册。

      下面是一个示例:

    自动注册Bean示例

      这个类和上面定义的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[]类型,默认为空数组。
  • 相关阅读:
    求最大子数组02
    求最大子数组
    第3周学习进度
    四则运算3
    第2周学习进度
    构建之法阅读笔记02
    四则运算2及单元测试
    四则运算1
    第1周学习进度
    构建之法阅读笔记01
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/3443722.html
Copyright © 2020-2023  润新知