zoukankan      html  css  js  c++  java
  • Spring入门(三)— AOP注解、jdbc模板、事务

    一、AOP注解开发

    1. 导入jar包

      aop联盟包、 aspectJ实现包 、 spring-aop-xxx.jar 、 spring-aspect-xxx.jar

    2. 导入约束

      aop约束

    3. 托管扩展类和被扩展类

      <!-- 要做AOP, 一定要托管扩展类和被扩展类 -->
       <bean id="us" class="com.pri.service.impl.UserServiceImpl"></bean>
       <bean id="logger" class="com.pri.util.Logger"></bean>   
    也可以使用注解来托管这两个类 。  @Component
    1. 在扩展出来的功能类身上和方法上打注解

    @Aspect //这个注解,是和aop有关,用于表示该类是一个功能扩展类,专门用来做增强的。
    public class Logger {
        @Before("execution(* com.pri.service.impl.*.*(..))")
        public void log(){
            System.out.println("输出日志了~~~!");
        }
    }

    二、 Jdbc模板

    • 为什么spring也要提供jdbc模板呢?

    spring是属于service层的框架, 它所处的位置比较尴尬,并且它想让自己更受程序员的喜爱。 除了能够做好自己的核心 IOC & AOP , 它还对前后两个框架都提供了支持。 spring其实对dao层的大部分技术有提供模板支持 。

    1. jdbc模板的入门

    public void testDemo(){
            
            //数据源,连数据库。 连什么类型的数据, 哪个库, 账号 & 密码
            DriverManagerDataSource dataSource  =new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql:///stus");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
    ​
            //1. 创建jdbc模板
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            
            String sql = "insert into t_user values(null , ? , ? )";
            
            //2. 执行添加操作,其实它的用法和以前的queryrunner一样。
            jdbcTemplate.update(sql , "66666","zhangsan");
    }

    2. crud操作

    • insert

    String sql = "insert into t_user values (null , ? , ?)";
    jdbcTemplate.update(sql, "heima24","6666");
    • delete 

     @Test
     public void testDelete(){
          //3. 执行添加操作
          String sql = "delete from t_user where uid = ?";
          jdbcTemplate.update(sql, 11);
     }
    • udpate

    @Test
    public void testUpdate(){
          //3. 执行添加操作
          String sql = "update t_user set password=? where uid=?";
          jdbcTemplate.update(sql, "888",11);
    }
    • findCount

    @Test
    public void testFindCount(){
         //3. 执行添加操作
         String sql = "select count(*) from t_user";
         int count = jdbcTemplate.queryForObject(sql, Integer.class);
         System.out.println("count="+count);
    }
    • findObject

    @Test
    public void testFindObject(){
           //3. 执行添加操作
          String sql = "select * from t_user where uid = ?";
          User user = jdbcTemplate.queryForObject(sql,new MyRowMapper()  , 10);
          System.out.println("user="+user);
    }
    • findList

    @Test
    public void testFindList(){
         //3. 执行添加操作
         String sql = "select * from t_user ";
         List<User> user = jdbcTemplate.query(sql, new MyRowMapper());
         System.out.println("user="+user);
    }

    查询要求我们手动封装class MyRowMapper implements RowMapper<User>{

    @Override
    public User mapRow(ResultSet rs, int arg1) throws SQLException {
            
            System.out.println("arg1=="+arg1);
            
            User user = new User();
            user.setUid(rs.getInt("uid"));
            user.setUsername(rs.getString("username"));
            user.setPassword(rs.getString("password"));  
            return user;
        }
    }

    3. jdbc模板的注入写法

    该小节演练的是三层结构的所有属性注入写法。(不写action | servlet)

    1. 分析

    以下代码其实是位于dao层的

      @Override
        public void save(User user) {
    ​
            // 数据源,连数据库。 连什么类型的数据, 哪个库, 账号 & 密码
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql:///stus");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
    ​
            // 1. 创建jdbc模板
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
    ​
            String sql = "insert into t_user values(null , ? , ? )";
    ​
            // 2. 执行添加操作,其实它的用法和以前的queryrunner一样。
            jdbcTemplate.update(sql, user.getPassword(), user.getUsername());
        }

    这套代码很多,而且它只是一个dao的操作代码而已。 如果我们还有别的dao方法。也要执行数据库操作,那么必然也得写出来这样的代码。 那么就存在冗余代码了。

    解决方案:

    1. 以前的解决方法:

      ​ 工具类 | 静态代码块

      1. 现在学了spring了,我想用spring的手法来简化dao的这一套代码。

        ​分析以上代码,其实它就是做了两个工作。

        ​a. 创建了jdbc模板的对象,并且完成了属性的赋值工作。

        ​b. 创建了dataSource 的对象,并且完成了属性的赋值工作。

    spring也可以完成这个工作, 创建对象,其实就是`IOC`的体现, 对属性的赋值工作其实就是`DI`的体现
    •~~~xml
    <bean id="jdbctemplate" class="">
        <property name="dataSource" ref="dataSource">
          
     <bean id="dataSource" class="">   
        <property name="..." value="">
        <property name="..." value="">
        <property name="..." value="">
        <property name="..." value="">
    •~~~

    2. 基本实现

    • service

    public class UserServiceImpl implements UserService {
        private UserDao userDao;
        public void setUserDao(UserDao userDao) {
             this.userDao = userDao;
        }
    ​
        @Override
        public void save() {
            System.out.println("调用了UserServiceImpl的save方法~~!");
    ​      userDao.save();
        }
    }
    • dao

    public class UserDaoImpl implements UserDao {
       private JdbcTemplate jdbcTemplate;
       public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
       }
       @Override
       public void save() {
            System.out.println("调用了UserDaoImpl的save方法~~");
            //3. 执行添加操作
            String sql = "insert into t_user values (null , ? , ?)";
            jdbcTemplate.update(sql, "heima242","0000");
    ​
        }
    }
    • xml

    <bean id="us" class="com.pri.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"></property>
    </bean><bean id="userDao" class="com.pri.dao.impl.UserDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
    </bean>
     
    <bean id="dataSource"    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql:///stus"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
    </bean>

    3. 整合c3p0连接池

    1. 导入jar包

           c3p0-0.9.5.2.jar   mchange-commons-java-0.2.12.jar

    1. 在xml中配置

    没有使用c3p0连接池的配置
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
      <property name="url" value="jdbc:mysql:///user"></property>
      <property name="username" value="root"></property>
      <property name="password" value="root"></property>
    </bean> ​ 使用了c3p0的配置 <!-- 使用c3p0连接池 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property> ​  <property name="jdbcUrl" value="jdbc:mysql:///user"></property>
      <property name="user" value="root"></property>
      <property name="password" value="root"></property>
    </bean>

    4. 使用jdbc.properties记录数据库连接信息

    为了方便修改数据库连接信息,通常在src下使用properties文件来记录他们。主要是方便修改

    1. 在src下新建一个properties文件,内容如下 名:jdbc.properties

    driverClass=com.mysql.jdbc.Driver
    jdbcUrl=jdbc:mysql:///user
    user=root
    password=root
    1. 在xml里面导入jdbc.properties ,并且在dataSource里面引用

    a. 导入context约束 
    b. 导入jdbc.properties
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 使用c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
         <property name="driverClass" value="${driverClass}"></property>
         <property name="jdbcUrl" value="${jdbcUrl}"></property>
         <property name="user" value="${user}"></property>
         <property name="password" value="${password}"></property>
    </bean>

    三、 事务管理

    1. 事务的回顾

    • 什么事务,事务有什么用?

    事务是用于包装一组操作的。当这一组操作里面的所有步骤都执行成功,那么这一组操作就算成功(事务的提交),如果有哪一个步骤执行失败,那么这一组操作就以失败告终。(事务的回顾)

    • 事务的特性

    原子性一致性隔离性持久性

    • 如果不考虑隔离级别,那么事务并发会出现以下问题

    1. 读的问题

      ​ 脏读 : 读到了另一个事务还没有提交的数据

      ​ 不可重复读 : 读到了另一个事务已经提交的数据 (针对的是update的数据)

      ​ 虚读| 幻读 : 读到了另一个事务已经提交的数据(针对的是insert的数据)

      解决方案: 设置隔离级别

      ​ 读未提交

      ​ 读已提交 Oracle

      ​ 可重复读 MySql

      ​ 序列化|串行化

    2. 写的问题

      ​ 丢失更新: 最后操作的事务,不管是提交还是回滚,都会让前一个是的数据丢失掉。

      解决方案:

      ​ 悲观锁 : 还没开始就认为一定会丢失更新。

      ​ 每一个事务在执行操作之前,先进行一次查询。 并且查询的语句里面带有关键字 for update

      ​ 乐观锁 : 认为不会出现丢失更新,

      ​ 要求程序员手动控制,给表额外添加一个字段 如: version。

    2. Spring对事务的管理办法

    • 事务应该写在哪一层? 为什么spring也要插手管理事务?

    事务应该写在Service层。 因为service表示的业务,一组业务可能会执行多个到的方法。所以在Service层声明事务更好一点。 而且spring正好是Service层的解决方案。

    • Spring针对事务的管理API

    Spring 可以管理事务, 但是真正完成操作的技术,spring可不管。 dao层使用的技术可能有很多。 jdbchibernatemybatis . 但是这几个框架,他们开启事务的办法,可能不一样。 spring为了统一管理事务,声明了一套规范出来,并且对底下的使用频率比较到的技术,都给出了具体的实现。


    管理事务的规范是 PlatformTransactionManager

    jdbc | mybatis : DataSourceTransactionManager
    hibernate : HibernateTransactionManager

    spring对dao层使用什么技术,它不管,它统一规定,要操作事务,必须使用管理员!!!

    3. spring对事务支持的写法

    提供了三种写法 编程式事务声明式事务(xml & 注解)

    1. 编程式事务

    纯靠写代码来完成事务配置

    @Test
    public void test(){  
        //事务的管理员是用来管理事务的,包含了打开事务、 提交事务、 回滚事务
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///stus");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
    
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
            
        //1. 创建事务的模板对象。
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(transactionManager);
            
        //事务事务的模板对象
        transactionTemplate.execute( new TransactionCallback<Object>() {
        //在事务里面执行这个方法。
        @Override
        public Object doInTransaction(TransactionStatus arg0) {
            //添加操作
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
                    
            String sql = "insert into  t_user values ( null , ? , ?)";
            jdbcTemplate.update(sql, "123","王五");
            int a = 1 / 0 ;
            jdbcTemplate.update(sql, "6666","赵六");
              return null;
           }
        });
       
    }

    2. 声明式事务(xml)

    xml方式配置事务,其实就是使用AOP的思想,在方法执行前,开启事务,方法执行后,提交事务(回滚事务)

    <!-- 以下属于事务的配置 如果要用事务了,那么事务的管理员是谁啊。 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
         <tx:advice id="advice01" transaction-manager="transactionManager">
            <tx:attributes>  <!-- 对前面的表达式找到的那一堆方法,进行过滤配置,表示谁才能用事务管理, 如果是* ,表示前面找到的那一堆都用事务管理 -->
                <tx:method name="save*"/>
                <tx:method name="update*"/>
                <tx:method name="delete*"/>
            </tx:attributes>
         </tx:advice>
         
         <!-- 
             以上配置,到目前为止,只是说明了,如果要开启事务,管理员是谁。 但是缺少了一个东西。
             就是哪一个方法到底要用事务呀? -->
         
         <aop:config>
            <aop:pointcut expression="execution(* com.pri.service.impl.*.*(..))" id="aa"/>
            
            <aop:advisor advice-ref="advice01" pointcut-ref="aa"/>
         </aop:config>

    3. 声明式事务(注解)

    使用注解的方式来开启事务

    • xml配置        

    1. 在xml中 声明注解事务的管理员
    ​
       <!-- 以下属于事务的配置 如果要用事务了,那么事务的管理员是谁啊。 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 指定注解事务使用的管理员是谁 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    • 代码配置

    2. 在业务逻辑类上或者方法上打注解
    ​  类上的注解,表示类中的所有方法都用事务, 如果在方法上打注解,表示只有这个方法才会用事务
    @Transactional
    public class UserServiceImpl implements UserService {
    
    }
    ​
    public class UserServiceImpl implements UserService {
        @Transactional
        @Override
        public void save() {}
    } 
  • 相关阅读:
    MYSQL转MSSQL
    SVN 服务器IP地址变更后客户端的修改
    gridview
    gridview外边距
    Android开发:自定义GridView/ListView数据源
    Android之Adapter用法总结
    collection set
    listview优化
    android应用开发全程实录-你有多熟悉listview
    android模块
  • 原文地址:https://www.cnblogs.com/gdwkong/p/8453163.html
Copyright © 2011-2022 走看看