二十一、Spring框架二之IoC的CRUD(1)

1 IoC综合案例(CRUD)—— 纯xml开发

开发流程概述

1 导入jar包依赖
2 创建pojo
3 创建service业务层接口
4 创建service业务层的接口实现类
		依赖注入,创建dao层对象
		对象引出创建dao层接口,方法与service接口方法完全一样
		service实现类的方法中利用dao对象调用dao实现类的对象
		
5 创建Sql配置文件
		复制接口中的方法,依次对应每个方法写出增删改查的sql语句

6 创建jdbc.properties连接数据库参数
7 创建spring核心配置文件ApplicationContext.xml
		编写Dao层
				加载properties配置文件
				配置Druid数据源:  bean对象,bean容器中
				配置SqlSessionFactory的对象: bean对象,bean容器中 
						数据源(ref用到Druid数据源的bean对象)
						pojo别名映射
						
				配置dao层动态代理对象生成的扫描器
						指定扫描的dao层路径
				
		
		编写Service层
				依赖注入:注入dao对象
					说明:上面配置dao层,已经扫描生成动态代理对象,已经在ioc容器中存放好
					对象名称默认是接口名称,首字母小写
					
8 编写junit测试类

2 Spring注解开发概述

2.1 Bean标签和注解的对应(定义Bean的对象)

  • bean标签对应注解@Component

  • 使用方法:加到类上,创建该类的对象,并放到ioc容器中

    • 注解属性value:bean标签的id属性
    • 不指定value属性,默认就是类名,首字母小写
    • 该注解衍生出了三个注解,@Controller,@Service,@Repository,用法和@Componet一致,为了更加清晰的提现层的概念。
  • bean标签属性scope对应注解@Scope

    • 注解属性value:singleton,prototype
  • bean标签属性init-method对应注解@PostConstruct

  • bean标签属性destroy-method对应注解@PreDestroy

tips:Spring只加载xml,不会自动扫描注解,xml中需要开启注解扫描

  • service层

    public interface AccountService {
        //模拟保存账户
        void save();
    }
    
    //@Component("accountService")
    @Service("accountService")
    @Scope("prototype")
    public class AccountServiceImpl implements AccountService {
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void saveAccount() {
            accountDao.saveAccount();
        }
    }
    
  • dao层

    public interface AccountDao {
        //模拟保存账户
        void save();
    }
    
    //@Component("accountDao")
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Override
        public void saveAccount() {
            System.out.println("保存了账户");
        }
    
        private void init() {
            System.out.println("AccountDao对象初始化");
        }
    
        private void destroy() {
            System.out.println("AccountDao对象 销毁了");
        }
    }
    
  • 添加applicationContext配置文件命名空间

    <?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"
    >
        
        <!-- 开启spring的注解扫描,扫描包中类的注解-->
        <context:component-scan base-package="com.itheima"></context:component-scan>
    </beans>
    
  • 测试注解

    @Test
    public void testIOC(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = context.getBean(AccountService.class);
        AccountDao accountDao = context.getBean(AccountDao.class);
    
        System.out.println(accountService);
        System.out.println(accountDao);
    
        context.close();
    }
    

2.2 依赖注入注解(对象初始化)

  • @Autowired注解(Spring框架提供)

    按照类型注入,如果无法确定唯一类型(接口有多个实现类),需要配合注解@Qualifier的使用

  • @Qualifier(“id”) 注解(Spring框架提供)

    • 按照id注入
  • @Resource注解(JDK提供)

    • 注解属性name:配置类的id
    @Resource(name="accountDao")
    private AccountDao accountDao;
    

代码演示

  • 业务层
@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements AccountService {

     @Autowired
    //@Qualifier("accountDao2")
    //@Resource(name = "accountDao2")
    private AccountDao accountDao;

    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }
}
  • dao层
package com.itheima.dao.impl;

/**
 * 账户dao实现类
 */
@Component("accountDao")
//@Repository("accountDao")
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {

    @Override
    public void saveAccount(Account account) {
        System.out.println("模拟转账");
    }

    @PostConstruct()
    public void init(){
        System.out.println("AccountDaoImpl 初始化...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("AccountDaoImpl 销毁...");
    }
}

2.3 Spring对Junit的支持

  • junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。
  • Spring对junit的支持,其实是自己实现了一个Runner对象(按照junit runner的要求实现)
  • Spring对junit的支持的体现
    • 好处一:配置完之后,不需要我们手动的启动Spring
    • 好处二:可以在junit测试类中使用@AutoWired等方式注入对象,直接对其进行调用测试
  • 使用步骤
    • 引入spring-test.jar
    • 配置测试类
  //Spring框架中的Runner对象, 替换Junit中的Runner对象
  @RunWith(SpringJUnit4ClassRunner.class)

  //框架启动入口, xml配置文件启动(2选1)
  @ContextConfiguration(locations = "classpath:beans.xml")
  //框架启动入口, 注解方式配置文件启动(2选1)
  //@ContextConfiguration(classes = SpringConfig.class)
  • 代码实现

    • pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>
    
    • 测试类
    package com.itheima;
    
    //Spring框架中的Runner对象, 替换Junit中的Runner对象
    @RunWith(SpringJUnit4ClassRunner.class)
    //框架启动入口, 注解方式配置文件启动
    //@ContextConfiguration(classes = SpringConfig.class);
    //框架启动入口, xml配置文件启动
    @ContextConfiguration(locations = "classpath:ApplicationContext.xml")
    public class AccountTest {
    
        //注入业务层接口
        @Autowired
        private AccountService service;
    
        @Test
        public void testIOC(){
    
            System.out.println("service = " + service);
            Account account = new Account();
            account.setName("小米");
            account.setMoney(888.0F);
            service.saveAccount(account);
        }
    }
    

3 IoC的综合案例(CRUD) —— 半注解半xml(真实开发)

3.1 简述流程

半注解半xml(删除了service层xml配置)
哪些bean对象用xml还是注解来配置:
	用第三方的使用xml配置
	我们自己编写的使用注解配置
	
案例:
1 dao层都是第三方Mybatis的使用xml,service层使用注解
2 @Service  依赖注入@Autowired,动态代理机制赋值对象

3.2 开发详解

企业主流的开发方式

注意:往往第三方jar中的对象我们使用xml配置(比如druid数据库连接池、Mybatis的SQLSessionFactory),类似于service层和dao层的实现类,这属于我们自己写的代码,往往会使用注解,这就是半xml半注解的模式。

  • applicationContext.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--配置dao-->
        <!--配置properties文件的位置-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--配置数据源-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
        <!--配置mybatis的SqlSessionFactory工厂-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="druidDataSource"></property>
            <property name="typeAliasesPackage" value="com.itheima.pojo"></property>
        </bean>
        <!--配置创建dao代理实现类的扫描器-->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.itheima.dao"></property>
        </bean>
    
        <!--配置service-->
        <!--让Spring开启注解扫描-->
        <context:component-scan base-package="com.itheima"></context:component-scan>
    </beans>
    
  • service层

    package com.itheima.service.impl;
    
    /**
     * 账户业务接口实现类
     */
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    
        //依赖注入
        @Autowired
        private AccountDao accountDao;
    
        //增删改查方法 无修改, 笔记中代码省略
        
    }
    
  • 测试类

    /*
    测试类
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        //测试类方法省略
        @Test
        public void findAll() {
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println("account = " + account);
            }
        }
    }
    

4 IoC的综合案例(CRUD) —— 纯注解开发(真实开发)

4.1 简述流程

纯注解开发(删除所有的配置文件:service层删除,dao层的配置删除)
1 修改dao层:将sql语句由原来的配置文件写入dao接口对应方法中@Insert @Select等
				开发中都是采用注解的形式,不是采用配置文件的方式

2 添加Spring的配置类(加上注解@Configuration表示是Spring的配置类) 
		@Bean把方法返回值对象放入到SpringIOC容器中
			加载properties配置文件
				@PropertySource({配置文件1的路径1},{配置文件2的路径2})
			
			配置数据源
				以方法的形式体现:返回数据源对象
				通过@Bean注解实现将方法返回值加入到Spring容器
				
			配置SQLSessionFactory
				以方法的形式体现:返回SqlSessionFactory对象
				@Bean注解
				@Qualifier配合指定Spring容器中传入哪个DataSource数据源对象,依赖注入
				
			dao扫描器动态代理对象
				配置扫描路径
				@Bean将对象返回放入到Spring容器中
				
			
			开启SpringIOC容器的注解扫描:扫描到service层上配置的注解
				@ComponentScan({要扫描的包的路径})
				
			
3 测试类
		@ContextConfiguration({classes={XXX.class}})加载注解形式的java文件


4 后期优化:
		将总的Spring配置文件进行按技术拆分子配置文件:
		SpringConfig.java: 总配置文件@import两个子配置文件
			子配置文件可以不用加@Configuration注解,总配置文件中加就可以了
			
		JDBCConfig.java:和jdbc相关的:配置数据源的
			加载外部的properties文件:@PropertySource({配置文件1的路径1},{配置文件2的路径2})
			通过@value注解获取properties文件中的值,为成员变量赋值
		MyBatisConfig.java:

4.2 相关注解说明

  • @Configuration标识当前类是Spring的一个配置类

  • @ComponentScan替代xml中的<context:component-scan/>

  • @Import引入其他配置类,被引入的配置类可以不加@Configuration注解

  • @PropertySource:引入外部properties文件,注意加classpath:

  • @Value对成员变量赋值

  • @Bean将一个方法的返回值对象加入到Spring的容器当中管理

  • @Qualifier可以使用在方法参数上,表明对应的形参引入/注入的对象类型

4.3 SpringConfig框架启动配置类

//Spring配置类,框架启动入口
@Configuration
//启动注解扫描
@ComponentScan({"com.itheima"})
public class SpringConfig {

    //方法的返回值, 加入到SpringIOC容器中管理
    @Bean("druidDataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/ssm_lx");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactoryBean createSqlSessionFactoryBean(@Qualifier("druidDataSource") DataSource ds){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(ds);
        sqlSessionFactoryBean.setTypeAliasesPackage("com.itheima.pojo");
        return sqlSessionFactoryBean;
    }

    @Bean("scannerConfigurer")
    public MapperScannerConfigurer createMapperScannerConfigurer(){
        MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
        scannerConfigurer.setBasePackage("com.itheima.dao");
        return scannerConfigurer;
    }
}

  • 测试
/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口, 注解方式配置文件启动(2选1)
@ContextConfiguration(classes = SpringConfig.class)
public class CRUDTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void findByName(){
        Account account = accountService.findByName("迪丽热巴");
        System.out.println("account = " + account);
    }

    @Test
    public void findAll(){
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

4.4 注解开发的SpringConfig配置优化

  • SpringConfig框架启动配置类

    /*
    作为Spring框架的主配置文件
     */
    @Configuration
    //开启Spring容器的注解扫描
    @ComponentScan({"com.itheima"})
    //导入子配置文件
    @Import({JDBCConfig.class, MybatisConfig.class})
    public class SpringConfig {
    
    }
    
  • JDBCConfig配置类

    //用于指定与数据库相关配置的配置文件
    @Configuration
    @PropertySource({"classpath:jdbc.properties"})
    public class JDBCConfig {
    
        @Value("${jdbc.driverClassName}")
        private String driverClassName;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        //配置数据源
        @Bean("DataSource")
        public DataSource createDataSource(){
            //创建Druid数据源
            DruidDataSource druidDataSource = new DruidDataSource();
            //配置相关信息(Driver, url, username, password)
            druidDataSource.setDriverClassName(driverClassName);
            druidDataSource.setUrl(url);
            druidDataSource.setUsername(username);
            druidDataSource.setPassword(password);
    
            return druidDataSource;
        }
    }
    
  • Mybatis配置类

    //用于配置与Mybatis相关的配置
    @Configuration
    public class MybatisConfig {
        //配置SqlSessionFactoryBean对象
        @Bean("sqlSessionFactory")
        public SqlSessionFactoryBean createSqlSessionFactoryBean(DataSource ds){
            //创建SqlSessionFactoryBean 对象
            SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
            //配置相关信息(数据源, pojo别名映射)
            sqlSessionFactory.setDataSource(ds);
            sqlSessionFactory.setTypeAliasesPackage("com.itheima.pojo");
    
            return sqlSessionFactory;
        }
    
        //配置dao的包扫描
        @Bean("scannerConfigurer")
        public MapperScannerConfigurer createMapperScannerConfigurer(){
            //创建MapperScannerConfigurer 对象
            MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
            //配置相关信息(扫描的dao包, 找到了dao层的接口文件, 找到了SQL映射文件, 生成接口实现类对象并存到Spring容器中)
            scannerConfigurer.setBasePackage("com.itheima.dao");
    
            return scannerConfigurer;
        }
    }
    
  • 测试类

    /*
    测试类
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    //加载的xml配置文件
    //@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
    
    //加载的注解形式的Spring主配置文件
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    	
        //其他方法省略
    
        @Test
        public void findAll() {
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println("account = " + account);
            }
        }
    }
    

5 Spring框架事务的管理

5.1 案例引入

转账案例

1 创建service接口
		创建接口方法transfer(name1, name2, money)
2 创建service接口实现类
		实现转账方法transfer
			获取两个目标账户对象
			-money  set  
			+money  set 
			更新数据库表里的金额

在实现转账transfer的过程中,如果出现错误,此时要实现事务回滚错误,保持事务的一致性。

5.2 常规做法

1 开启事务
2 提交事务
3 回滚事务
4 释放连接

service层中对事务的操作添加事务的控制:
这里创建了管理事务的工具类,将事务的处理以及异常捕捉的处理放到了工具类中来优化代码。

  public void transfer(String resource, String target, double money)
  {
    try{
      this.transactionManager.begin();
      Account resourceAccount = this.accountDao.findByName(resource);
      System.out.println("当前线程:" + Thread.currentThread().getName());
      Account targetAccount = this.accountDao.findByName(target);
      
      resourceAccount.setMoney(Double.valueOf(resourceAccount.getMoney().doubleValue() - money));
      targetAccount.setMoney(Double.valueOf(targetAccount.getMoney().doubleValue() + money));
      
      this.accountDao.update(resourceAccount);
      System.out.println("当前线程:" + Thread.currentThread().getName());
      int i = 1 / 0;
      this.accountDao.update(targetAccount);
      

      this.transactionManager.commit();
    } catch (Exception e) {
      e.printStackTrace();
      this.transactionManager.rollback();
    } finally {
      this.transactionManager.close();
    }
  }

5.3 出现的问题

以上会出现事务控制不成功。
如何保证使用同一个连接来做的事务?
不从连接池中去取连接,从当前主线程(绑定的连接对象)获取连接对象。
ThreadLocal解决该问题:在当前线程上绑定局部变量,实现当前线程的任意执行位置获取绑定对象的值
使用案例:service和dao代码交互:获取连接不是在同一个java类中,使用ThreadLocal来绑定连接对象。

5.4 ThreadLocal使用介绍

使用ThreadLocal来绑定一个字符串,使用字符串的时候get能够得到。
在这里插入图片描述

5.5 Spring中事务的控制

1 配置数据源

配置Connection对象 createConnection
解决原理:
		当前的事务代码Connection对象 与 Mybatis中的SQLSession对象中的Connection对象 不是同一个连接对象
		使用ThreadLocal技术, 实现 SQLSession对象 与 事务代码Connection对象  是同一个
		通过Spring框架来实现这个操作 Connection对象  是 同一个
@Configuration
@PropertySource({"classpath:jdbc.properties"})
public class JDBCConfig {

    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    //配置数据源
    @Bean("druidDataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        //配置相关信息(驱动, url, 用户名, 密码)
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);

        return druidDataSource;
    }

    //配置Connection对象
    @Bean
    public Connection createConnection(DataSource dataSource){
       /*
       当前的事务代码Connection对象 与 Mybatis中的SQLSession对象中的Connection对象 不是同一个连接对象
       使用ThreadLocal技术, 实现 SQLSession对象 与 事务代码Connection对象  是 同一个
       通过Spring框架来实现这个操作 Connection对象  是 同一个
        */
        //1. 通过Spring框架提供的事务同步管理器, 实现事务代码Connection对象 与 Mybatis中的SQLSession对象中的Connection对象 同一个
        //initSynchronization()方法中, 代码底层源码中, 通过 ThreadLocal实现了 当前线程与 Connection对象进行绑定
        TransactionSynchronizationManager.initSynchronization();
        // Spring框架提供了一个 DataSourceUtils工具类, getConnection()方法,实现了从ThreadLocal 中 绑定的Connection对象
        Connection connection = DataSourceUtils.getConnection(dataSource);
        return connection;
    }
}

2 注入连接对象

//依赖注入
@Autowired
private Connection connection;

管理事务工具类的完整代码:

/*
    管理事务的工具类
 */
@Component
public class TransactionManager {

    //依赖注入
    @Autowired
    private Connection connection;

    //开启事务
    public void begin() {
        try {
            //当前当前代码执行所在的线程对象
            //System.out.println("当前线程:"+Thread.currentThread().getName());
            System.out.println("开启事务connection = " + connection);

            connection.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public void commit() {
        try {
            //当前当前代码执行所在的线程对象
            //System.out.println("当前线程:"+Thread.currentThread().getName());
            System.out.println("提交事务connection = " + connection);

            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //回滚事务
    public void rollback() {
        try {
            //当前当前代码执行所在的线程对象
            //System.out.println("当前线程:"+Thread.currentThread().getName());
            System.out.println("回滚事务connection = " + connection);

            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //释放连接
    public void close() {
        try {
            //当前当前代码执行所在的线程对象
            //System.out.println("当前线程:"+Thread.currentThread().getName());
            System.out.println("释放连接connection = " + connection);

            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

6 IDEA使用小技巧

配置模板,利用快捷键生成常用的代码,避免最大程度的出错

开发小技巧:
	settings中:
	配置keyMap快捷键,提示:basic
	配置常用代码块模板:template——> code template 新建模板 
		在我们用的时候new中会出现
	配置哪些模板:
		Druid Pool连接数据库的配置文件
		日志log4j
		MyBatis Mapper的映射文件
		MyBatis SqlMapConfig的核心配置文件
		找Maven Project.xml,将java的编译版本配置项properties放进去
			pom.xml中maven.compiler.source 1.8
		
	idea配置还原:
		路径位置:当前/用户/IntelliJidea/config和system两个文件夹删了,重新激活idea,代价是模板、快键键都要重新配置
		
	Maven中创建module,不能创建project中创建module,project和module是平行的
	学会debug:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值