SpringBoot-day3

1.SpringBoot 持久化支持(MyBatis)

1.1 XML版本

1.1.1 步骤分析

  1. 新建maven project;
  2. 在pom.xml中引入相关依赖
    • (1)基本依赖,jdk版本号;
    • (2)mysql驱动,mybatis依赖包,mysql分页PageHelper:
  3. 创建启动类App.java
  4. 在application.properties添加配置文件;
  5. 创建数据库和表
  6. 编写domain的测试类User;
  7. 编写UserMapper;
  8. 编写mybatis的xml文件
  9. 编写IUserService接口和接口的实现类UserServiceImpl
  10. 编写UserController
  11. 编写测试方法,测试service层
  12. 启动项目,测试controller层
  13. 配置事务。注解式事务
  14. 测试事务
  15. 获取自增的主键id
  16. 使用插件PageHelper分页

1.1.2 实现

  1. 新建maven project;
  2. 在pom.xml中引入相关依赖
    • (1)基本依赖,jdk版本号
    • (2)mysql驱动,mybatis依赖包,mysql分页PageHelper
<!--子模块中配置导包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
<!-- mysql 数据库驱动. -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--
                    spring-boot mybatis依赖:
                    请不要使用1.0.0版本,因为还不支持拦截器插件,
                    1.1.1 是博主写帖子时候的版本,大家使用最新版本即可
                 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!--
                MyBatis提供了拦截器接口,我们可以实现自己的拦截器,
                将其作为一个plugin装入到SqlSessionFactory中。
                Github上有位开发者写了一个分页插件,我觉得使用起来还可以,挺方便的。
                Github项目地址: https://github.com/pagehelper/Mybatis-PageHelper
             -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.1.0</version>
        </dependency>
  1. 创建启动类App.java
package cn.wangningbo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("cn.wangningbo.mapper")//注意这个注解,使用mybatis扫描包的mapper包
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
  1. 在resources下创建配置文件application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: apy06942
mybatis:
  type-aliases-package: cn.wangningbo.domain,cn.wangningbo.query
  1. 创建数据库和表
  2. 编写domain的测试类User;
package cn.wangningbo.domain;

public class User {
    private Long id;
    private String name;
    //提供get方法、set方法、无参构造方法和一个name参数的构造方法、toString方法
}
  1. 编写UserMapper;
package cn.wangningbo.mapper;

import cn.wangningbo.domain.User;

import java.util.List;

public interface UserMapper {
    void save(User user);

    List<User> loadAll();
}
  1. 编写mybatis的xml文件

在resources下创建目录cn/wangningbo/mapper,在此目录下创建相应的映射xml,UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--查询-->
<mapper namespace="cn.wangningbo.mapper.UserMapper">
    <insert id="save" parameterType="cn.wangningbo.domain.User">
        INSERT into t_user (name) values (#{name})
    </insert>
    <select id="loadAll" resultType="User">
        SELECT * from t_user
    </select>
</mapper>
  1. 编写IUserService接口和接口的实现类UserServiceImpl
package cn.wangningbo.service;

import cn.wangningbo.domain.User;

import java.util.List;

public interface IUserService {
    void add(User user);

    List<User> getAll();
}
package cn.wangningbo.service.impl;

import cn.wangningbo.domain.User;
import cn.wangningbo.mapper.UserMapper;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;

    public void add(User user) {
        userMapper.save(user);
    }

    public List<User> getAll() {
        return userMapper.loadAll();
    }
}
  1. 编写UserController
package cn.wangningbo.controller;

import cn.wangningbo.domain.User;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;

    //保存由测试做就行,这里就不写页面了
    //查询所有
    @GetMapping
    public List<User> getAll() {
        return userService.getAll();
    }
}
  1. 编写测试方法,测试service层
package cn.wangningbo.service;

import cn.wangningbo.App;
import cn.wangningbo.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class IUserServiceTest {
    @Autowired
    private IUserService userService;

    @Test
    public void add() {
        userService.add(new User("二狗"));
        userService.add(new User("张三"));
    }

    @Test
    public void getAll() {
        List<User> list = userService.getAll();
        for (User user : list) {
            System.out.println(user);
        }
    }
}
  1. 启动项目,测试controller层
http://localhost:8080/user
  1. 配置事务。注解式事务

在service层加入事务

//类级别值只读事务
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)

//在类里面写方法上面打写事务,下面这两种的效果是一样的
//@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@Transactional

package cn.wangningbo.service.impl;

import cn.wangningbo.domain.User;
import cn.wangningbo.mapper.UserMapper;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

//类级别值只读,在类里面写方法上面打写事务
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;

    //@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    @Transactional
    public void add(User user) {
        userMapper.save(user);
        // int i = 1/0;
    }

    public List<User> getAll() {
        return userMapper.loadAll();
    }
}
  1. 测试事务

由于在service那里的add方法弄了一个int i = 1/0;事务成功了就不会添加到数据库,注释掉那一行 int i = 1/0;就会添加到数据库。

  1. 获取自增的主键id

只需要在Mapper.xml里面的添加方法配置一下useGeneratedKeys="true" keyProperty="id" keyColumn="id"

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.wangningbo.mapper.UserMapper">
    <insert id="save" parameterType="cn.wangningbo.domain.User"
            useGeneratedKeys="true"
            keyProperty="id"
            keyColumn="id">
        INSERT into t_user (name) values (#{name})
    </insert>
    <select id="loadAll" resultType="User">
        SELECT * from t_user
    </select>
</mapper>

测试是否获得主键id,执行测试方法输出查看是否有id

    @Test
    public void add() {
        User user = new User("二狗");
        System.out.println(user.getId());
        userService.add(user);
        System.out.println(user.getId());
    }

输出结果:
null
6

  1. 使用插件PageHelper分页

PageHelper是mybatis一个插件,可以用它完成分页

步骤:导入jaar,在配置类里面配置Bean,测试

导入jar

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.1.0</version>
        </dependency>

配置

package cn.wangningbo.config;

import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

@Configuration
public class MyConfig {
    @Bean
    public PageHelper pageHelper() {
        System.out.println("MyBatisConfiguration.pageHelper()");
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        pageHelper.setProperties(p);
        return pageHelper;
    }
}

service层进行分页

    public List<User> getAll() {
        PageHelper.startPage(1, 3);
        return userMapper.loadAll();
    }

测试

@Test
    public void getAll() {
        //此时list已经不是传统list,相当于原来pageList
        Page<User> list = (Page<User>) userService.getAll();
        //class com.github.pagehelper.Page
        System.out.println(list.getClass());

        //还有分页信息
        System.out.println(list.getTotal());
        System.out.println(list.getPageNum());

        //获取数据:本身也是一个继承与ArraList的List
        for (User user : userService.getAll()) {
            System.out.println(user);
        }
    }

输出结果

class com.github.pagehelper.Page
5
1
User{id=1, name='二狗'}
User{id=2, name='张三'}
User{id=4, name='二狗'}

1.2 注解版

注解版与xml版很相似,我只简单说几句不一样的地方

  1. 注解版使用mybatis的时候,只需要在Mapper接口那里的方法头上打注解即可,以前使用xml版本的时候都是在xml里面写的查询,添加等方法!
package cn.wangningbo.mapper;

import cn.wangningbo.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    @Insert("insert into t_user(name) values (#{name})")
    void save(User user);

    @Select("select * from t_user")
    List<User> loadAll();
}

2. SpringBoot启动分析

2.1 创建SpringApplication对象

initialize(sources);
private void initialize(Object[] sources) {
    //保存主配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    //判断当前是否一个web应用
    this.webEnvironment = deduceWebEnvironment();
    //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //从多个配置类中找到有main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.2 运行run方法

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
    
   //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的获取SpringApplicationRunListener.starting()方法
   listeners.starting();
   try {
       //封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
            //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
       
      Banner printedBanner = printBanner(environment);
       
       //创建ApplicationContext;决定创建web的ioc还是普通的ioc
      context = createApplicationContext();
       
      analyzers = new FailureAnalyzers(context);
       //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
       //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
       //回调所有的SpringApplicationRunListener的contextPrepared();
       //
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
       
       //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
       //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
      refreshContext(context);
       //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
       //ApplicationRunner先回调,CommandLineRunner再回调
      afterRefresh(context, applicationArguments);
       //所有的SpringApplicationRunListener回调finished方法
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
       //整个SpringBoot应用启动完成以后返回启动的ioc容器;
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容