栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring

面向企业开发基础 一 Maven 1 概要
  • 导入依赖
  • 项目结构
  • 项目后期 —> 测试 —> 打包 —> 部署 —> 多模块开发
2 作用
  • 解决依赖问题

  • 规范项目结构

  • 解决项目后期的打包部署等一些列工作

3 什么是Maven

Maven是使用java编写的开源项目管理工具 POM.xml Project Object Model

为什么需要Maven?

1.项目管理问题:项目中jar包资源越来越多,jar包的管理越来越沉重

2.繁琐:腰围每个项目手动导入所需的jar,需要搜集全部jar

3.复杂:项目中的jar如果需要版本升级,就需要再重新搜集jar

4.冗余:相同的jar在不同的项目中保存了多份

Maven解决了什么问题?

1.添加第三方jar包

2.jar包之间的依赖关系

3.处理jar包之间的冲突

4.获取第三方jar包

5.将项目拆分成多个工程模块

6.实现项目的分布式部署

4 其他构建技术
  • Ant 构建 – build.xml
  • Maven构建 – pom.xml
  • Gradle构建 – build.gradle
5 使用
  • 下载maven

  • 配置maven

    • localRepository
    
    
       
      D:toolsrepo
      
      
      
      
      
      
      
      
      
        
            nexus-aliyun
            central
            Nexus aliyun
            https://maven.aliyun.com/repository/central 
        
      
      
      
    
    
  • 用户配置

    将配置号的settings.xml文件复制到用户下面的.m2目录下

二 Spring IoC

IoC:Inversion of Control,控制反转,把创建对象的控制权交给第三方容器

如何将创建对象的控制权交个容器

1.导入依赖


    
      org.springframework
      spring-context
      5.3.10
    
  

2.spring-context.xml


3.从容器中获取创建的类,源码是使用xmlDOM操作通过id获取

public class iocTestMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        UserDaoImp userDao = classPathXmlApplicationContext.getBean("userDao", UserDaoImp.class);
    }
}
//默认查找classpath路径下的文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml");
//多文件,也可传递数组
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml","spring/spring-ioc2.xml",.....);

//默认为项目工作路径 即项目的根目录 
FileSystemXmlApplicationContext applicationContext=
                new FileSystemXmlApplicationContext("/src/main/resources/spring/spring-ioc.xml");

//也可以读取classpath下的文件
FileSystemXmlApplicationContext applicationContext=new FileSystemXmlApplicationContext("classpath:spring/spring-ioc.xml");

//使用前缀file 表示的是文件的绝对路径 
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("file:D:/app.spring.xml"); 
//多文件与ClassPathXmlApplicationContext相同

相关源码

if (beanProperty.element("map") != null) {   
                    Map propertiesMap = new HashMap();   
                    Element propertiesListMap = (Element) beanProperty   
                            .elements().get(0);   
                    Iterator propertiesIterator = propertiesListMap   
                            .elements().iterator();   
                    while (propertiesIterator.hasNext()) {   
                        Element vet = (Element) propertiesIterator.next();   
                        if (vet.getName().equals("entry")) {   
                            String key = vet.attributeValue("key");   
                            Iterator valuesIterator = vet.elements()   
                                    .iterator();   
                            while (valuesIterator.hasNext()) {   
                                Element value = (Element) valuesIterator.next();   
                                if (value.getName().equals("value")) {   
                                    propertiesMap.put(key, value.getText());   
                                }   
                                if (value.getName().equals("ref")) {   
                                    propertiesMap.put(key, new String[] { value   
                                            .attributeValue("bean") });   
                                }   
                            }   
                        }   
                    }   
                    bean.getProperties().put(name, propertiesMap);   
                }

public static Object newInstance(String className) {
		Class cls = null;
		Object obj = null;
		try {
			cls = Class.forName(className);
			obj = cls.newInstance();
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		return obj;

}
DI

DI:Dependency Injection,依赖注入

如何注入?

1.
2.

        
        
        

1.spring-context.xml IoC容器





        
	

2.userService声明中多出了一个property的标签,这个标签指向了我们刚才创建的userDao对象,它的作用是把userDao对象传递给userService实现类中的userDaoImp属性,该属性必须拥有set方法才能注入成功,我们把这种往类userService对象中注入其他对象(userDaoImp)的操作称为依赖注入,其中的id或者name必须与userService实现类中变量名称相同,到此我们就完成对需要创建的对象声明。

通过源码得知,对于非集合类型的属性,底层代码大量使用了JDK的反射和内省机制,通过属性的getter方法(reader method)获取指定属性注入以前的值,同时调用属性的setter方法(writer method)为属性设置注入后的值,所以说为什么要在类里面设置set方法。

public class UserServiceImp implements UserService {
    //userDaoImp为注入的对象
    private UserDaoImp userDaoImp;

    //注入userDaoImp对象,需要set方法
    public void setUserDaoImp(UserDaoImp userDaoImp) {
        this.userDaoImp = userDaoImp;
    }

    @Override
    public Integer login() {
        return userDaoImp.insert();
    }
}
注册对象

1.xml

2.注解

3.Java Config

1.xml

2.导入依赖

spring-context



    


    
        

    

    
    
    


    

    
        
            
                2
                22
                5
            
        
        
            
                
            
        
        
        
    

    
    
        12
        23
    
    
        
    

public class iocTestMain2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        TestPojo pojo = classPathXmlApplicationContext.getBean("pojo", TestPojo.class);
        System.out.println("pojo = " + pojo);
    }
}
//结果:
//初始化
//pojo = TestPojo{list=[2, 22, 5], map={name=张三}, name='小明', age=12}
public class iocTestMain2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        TestLife life = classPathXmlApplicationContext.getBean("life", TestLife.class);
        classPathXmlApplicationContext.close();
    }
}
//结果:
//初始化
//销毁
public class TestLife {
    public TestLife() {
    }

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

    public void destroy(){
        System.out.println("销毁");
    }
}
属性 init-method


destroy-method


id

声明对象的在容器中的键

class

在容器需要创建对象的类

scope

对象的作用域

  1. singleton 单例对象 默认值
  2. prototype 每次从容器中获取对象都是新创建对象
  3. request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境
  4. sesison 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境
  5. globalSession 为每个全局的HTTP会话创建一个实例
初始化赋值
  • 通过constructor-arg
  • 通过property子标签

简写方式

  • p 表示 property的缩写
xmlns:p="http://www.springframework.org/schema/p"
  • util 声明 list map array
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
">
2.注解

spring-ioc-注解

  • 必须开启注解支持,在xml文件中使用 context:component-scan

xmlns:context="http://www.springframework.org/schema/context"



使用原则

1.能使用注解尽量使用注解

2.第三方框架 xml方式

核心注解 注册到容器
  1. @Component 通用注解,当一个类需要注入的容器中,没有特定的语义可以使用该注解
  2. @Service 在业务层使用
  3. @Repository 使用在数据层
  4. @Controller 在控制层
public class iocTestMain2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context1.xml");
        UserService userServiceImp = classPathXmlApplicationContext.getBean("userServiceImp", UserService.class);
        System.out.println("userServiceImp = " + userServiceImp);
    }
}

//如果括号里面内容为空,默认名字为类名的首字母小写,即userServiceImp,特殊情况为如果类的名字开头几个字母都为大写,则原封不动的命名,如ABUserServiceImp
@Service()
public class UserServiceImp implements UserService {
    private UserDaoImp userDaoImp;

    public void setUserDaoImp(UserDaoImp userDaoImp) {
        this.userDaoImp = userDaoImp;
    }

    @Override
    public Integer login() {
        return userDaoImp.insert();
    }
}
依赖注入
  • @Autowired
  • @Resource
  1. 构造方法注入
  2. setter方法注入
  3. 属性注入
    3.1 Autowired spring 自带的注解
  • Autowired 通过类型去查找依赖对象,如果类型不存在或者存在多个类型直接报错

3.2 Resource jsr250定义的

  • 在不使用任何属性的情况下 先根据名字查找对象(属性的名字) 如果通过属性名找不到依赖对象直接通过类型查找,如果类型不存在或者存在多个类型直接报错

  • 如果指定了name属性的值 只会通过name属性来查找对象,如果找不到直接抛出异常

  • 如果指定了type属性的值 只会通过类型去查找对象,如果查找类型不存在或者存在多个 直接抛出异常
    3.3 Inject jsr330定义的

@Service()
public class UserServiceImp implements UserService {
    @Autowired
    @Resource
    private UserDaoImp userDaoImp;

    public void setUserDaoImp(UserDaoImp userDaoImp) {
        this.userDaoImp = userDaoImp;
    }

    @Override
    public Integer login() {
        return userDaoImp.insert();
    }
}
其他注解

@Qualifier

配合Autowired使用,通过名字来查找依赖对象

@Primary

如果容器中存在多个实现类,可以使用@Primary注解来提高优先级

Java Config
@Configuration
public class DruidConfig {
    @Bean
    public DataSource dataSource() throws Exception {
        HashMap map = new HashMap<>();
        map.put("username", "root");
        map.put("password", "root");
        map.put("url", "");
        return DruidDataSourceFactory.createDataSource(map);
    }
}
三 spring-MVC

Spring的web模型 - 视图 - 控制器(MVC)

  • 前后端分离

    1. 浏览器 —>发送请求—> 请求前端xxx.html界面 —>浏览器解析–>js 图片 ajax–>请求服务(ajax)–>请求服务接口
    1. 后台 --> 控制层 —>业务层 —>数据层 -->数据库
  • 响应数据 --> json

    1. 前端—> 接收解析json -> DOM操作—>展示数据
  • 前后端不分离

    1. 浏览器—> 请求后台路径 (http://localhost:8080/anno/body) —> 控制层 —>业务层---->数据层—>数据库

      浏览器发送请求->DispatcherServlet(HandlerMapping里面有键值对,键为路径,值为Controller,然后找到Controller的核心方法)->业务层->数据层->数据库

  • 渲染 xxx.jsp + 数据 – > xxx.html

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型。

前端控制器是DispatcherServlet;应用控制器其实拆为处理器映射器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;页面控制器/动作/处理器为Controller接口(仅包含ModelAndView handleRequest(request, response) 方法)的实现(也可以是任何的POJO类)(“普通Java对象”(Plain Old Java Object)的定义,简称POJO)

在Spring的Web MVC框架提供了模型 - 视图 - 控制器架构以及可用于开发灵活,松散耦合的Web应用程序准备的组件。 MVC模式会导致分离的应用程序(输入逻辑,业务逻辑和UI逻辑)的不同方面,同时提供这些元素之间的松耦合。

模型(Model )封装了应用程序的数据和一般他们会组成的POJO。

视图(View)是负责呈现模型数据和一般它生成的HTML输出,客户端的浏览器能够解释。

控制器(Controller )负责处理用户的请求,并建立适当的模型,并把它传递给视图渲染。

DispatcherServlet

Spring的web模型 - 视图 - 控制器(MVC)框架是围绕着处理所有的HTTP请求和响应的DispatcherServlet的设计。 Spring的Web MVC框架的DispatcherServlet的请求处理流程说明如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btHo3ryM-1653495489176)(E:培训第二阶段总结pictruemvc.webp)]

下面是对应于传入的HTTP请求到DispatcherServlet的事件序列:

1.接收HTTP请求后,DispatcherServlet 咨询 HandlerMapping 来调用相应的控制器。DispatcherServlet(HandlerMapping里面有键值对,键为路径,值为Controller,然后找到Controller的核心方法)

2.该控制器接受请求并调用基于使用GET或POST方法相应的服务方法。服务方法将基于定义的业务逻辑设置模型数据,并返回视图名到DispatcherServlet。

3.DispatcherServlet将需要帮助的ViewResolver从拾取到该请求所定义的视图。

4.一旦视图被敲定,DispatcherServlet会传递模型数据是在浏览器上最终呈现的视图。

所有上述部件,即HandlerMapping,控制器和视图解析器WebApplicationContext 部分是纯的 ApplicationContext 必要的 Web应用程序的一些额外的功能扩展。

作用

Spring 进行web开发

对Servlet进行封装简化

如何使用 1 导入依赖


    org.springframework
    spring-webmvc
    5.3.9

2 配置文件

spring-context.xml

  • context:component-scan


    
    

spring-mvc.xml

  • context:component-scan
  • mvc:annotation-driven


    
    
    
    



web.xml

  • Spring其他的配置文件初始化
  • 注册DispatchServlet
    • 初始化配置文件



    Archetype Created Web Application
    
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
    
    
        contextConfigLocation
        classpath*:spring-context.xml
    
    
 
    
        dispatcherServlet
        org.springframework.web.servlet.DispatcherServlet
        
         
        
            contextConfigLocation
            classpath:spring-mvc.xml
        
    
    
    
    
        dispatcherServlet
        
@RequestMapping("show16")
public ModelAndView test16(@RequestParam("name")String name){
    ModelAndView mv = new ModelAndView();
    mv.setViewName("hello2");
    mv.addObject("msg", "接收普通的请求参数:" + name);
    return mv;
}
 

@RequestMapping("show17")
public ModelAndView test17(@RequestParam(value="name",required=false)String name){
    ModelAndView mv = new ModelAndView();
    mv.setViewName("hello2");
    mv.addObject("msg", "接收普通请求参数:" + name);
    return mv;
}
 

@RequestMapping("show18")
public ModelAndView test18(@RequestParam(value="name",required=true,defaultValue="hello")String name){
    ModelAndView mv = new ModelAndView();
    mv.setViewName("hello2");
    mv.addObject("msg", "接收普通请求参数:" + name);
    return mv;
}

批量删除

@GetMapping("/del")
public String del(@RequestParam List ids) {
    ids.forEach(System.out::println);
    return "list作为参数";
}
作用
  1. 给参数起别名
  2. 默认情况下是必要参数
  3. 给参数设置默认值,当设置了默认值,必要参数就会设置成false
源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
	@AliasFor("name")
	String value() default "";//给参数起别名
	@AliasFor("value")
	String name() default "";
	boolean required() default true;//是否是必要参数,默认是必要参数
	String defaultValue() default ValueConstants.DEFAULT_NONE;//给参数设置默认值
}

属性说明

属性说明
defaultValue给参数设置默认值
value给参数起别名
required是否是必要参数,默认是必要参数
@ReqeustBody 作用

获取请求体中的数据,只能用POST请求和PUT请求

应用场景 通常情况下接收客户端的json数据自动转化对象

@ResponseBody 通常情况下服务端发送的对象自动转化成json数据

源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
	boolean required() default true;
}

例子
@Data
public class UserReqeustParams {
    @JSONField(name = "user_name")
    private String username;
    private int page;
    private int size;
    @JSONField
    private String password;
    private String phone;
    @JSONField(deserialize = false)
    private List addressList;
    @JSONField(format = "yyyy-MM-dd")
    private Date createData;
    @JSONField(serialize = false)
    private boolean status;
}
   @PostMapping("/body")
    @ResponseBody
    public String body(@RequestBody UserReqeustParams user) {
        System.out.println(user.toString());
        return "接受客服端的json数据";
    }

参数

{
  "user_name": "admin",
  "page": 1,
  "size": 10,
  "password": "123456",
  "phone": "123456",
  "addressList": [1,2,3,4],
  "createData": "2021-09-24 15:39:25",
  "status": true
}
集成fastjson 导入依赖

    com.alibaba
    fastjson
    1.2.78

配置

    
        
            
                
                    
                    application/json;charset=UTF-8
                
            
            
                
                    
                    
                
            
        
    

注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface JSONField {
	// 设置别名
    String name() default "";
    //设置日期格式
    String format() default "";
    //  是否序列化,序列化就是对象转json格式
    boolean serialize() default true;
    // 是否反序列化,反序列化就是json转对象
    boolean deserialize() default true;
}
@Data
public class UserReqeustParams {
    //fastjson传过来的名字为user_name,就需要设置别名
    @JSONField(name = "user_name")
    private String username;
    
    private int page;
    private int size;
    
    @JSONField
    private String password;
    private String phone;
    
    @JSONField(deserialize = false)
    private List addressList;
    
    //设置日期格式
    @JSONField(format = "yyyy-MM-dd")
    private Date createData;
    
    @JSONField(serialize = false)
    private boolean status;
}
@PathVariable 作用

从请求路径中获取参数信息

注意:

RESTFul风格

  • 只能声明一些简单的 参数 例如 id page size 等 经常使用在get delete 请求
源代码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
	@AliasFor("name")
	String value() default "";
	@AliasFor("value")
	String name() default "";
	boolean required() default true;
}
属性说明
属性说明
value/name给参数起别名
required是否是必要参数, 默认必要参数
例子
	// product/1
    @GetMapping("/product/{pid}")
    @ResponseBody
    public String body(@PathVariable("pid") Long id) {
        return "接受客服端的json数据";
    }
	
	
    //  list/2/20
    @GetMapping("/list/{page}/{size}")
    @ResponseBody
    public String list(@PathVariable int page, @PathVariable int size) {
        return "接受客服端的json数据";
    }
三 响应内容 概要
  1. 前后分离
  2. 前后端不分离
  • 前后端分离

    1. 浏览器—发送请求— 请求前端xxx.html界面 —>浏览器解析–js 图片 ajax-- 请求服务(ajax)–请求服务接口
    1. 后台 --> 控制层 —>业务层 —>数据层 -->数据库
  • 响应数据 --> json

    1. 前端—> 接收解析json -> DOM操作—>展示数据
  • 前后端不分离

    1. 浏览器-> 请求后台路径 (http://localhost:8080/anno/body) —> 控制层 —>业务层---->数据层—>数据库

      浏览器发送请求->DispatcherServlet(HandlerMapping里面有键值对,键为路径,值为Controller,然后找到Controller的核心方法)->业务层->数据层->数据库

  • 渲染 xxx.jsp + 数据 – > xxx.html

分离
  1. 核心注解 @RestController = @Controller + @ResponseBody
  2. 在类上面使用 @RestController
  3. 方法的返回值可以是任意对象 不需要进行json转化

注意:

  • 依赖于第三方json框架 fastjson jackson gson
不分离
  1. 可以返回字符串 也可以是ModelAndView对象
  2. 转化跟重定向
    1. 转发 forward:站内路径
    2. 重定向 redirect:“站内路径|站外路径”

注意:

  • 配置视图解析器

    
    
    
    
    
Mybatis 一 概要

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

二 传统JDBC开发的缺点
  1. 频繁的建立连接释放资源 可以复用连接对象,提供性能 (连接池)

  2. SQL语句跟Java代码耦合在一起

    • 需求一旦发生改变,维护起来比较困难
    • 可读性比较差

    解决方案

    • 将SQL语句跟Java代码分离
  3. JDBC参数问题 存在一定的局限性

    1. 传递参数的顺序
    2. 条件判断

    解决方案

    1. 对参数进行封装,不需要关注参数的传递顺序
    2. 动态SQL
  4. 结果集处理

    1. 从rs对象获取值,在封装成对象

    解决方案

    1. 对结果集的处理进行统一的封装 (将数据的结果集转化成对象)
备注

ORM 是 (Object Relation Mapping)

用户面向对象的方式操作数据库

  • 类名 —> 表名
  • 属性 —> 列名
  • 类型 ---- 类型
  • 对象 ----> 记录

当列名跟属性,表名跟类命名不一致的情况下该怎么办?

  • 注解
  • ResultMap
三 基础使用 1 导入依赖

    	
    
        org.mybatis
        mybatis
        ${mybatis.version}
    
    	
    
        mysql
        mysql-connector-java
        ${mysql.version}
    
    

2 核心配置文件



    
    
        
            
            
                
                
                
                
            
        
    

3 定义mapper接口
public interface UserMapper {
    
    List selectList();
	
    int insert(User user);
}
4 定义mapper.xml文件
  • insert标签

  • 参数处理

    • 在xml文件中parameterType属性
    • 在接口中使用注解 @Param
  • 在mapper,xml文件中如何使用参数

    #{变量}




    
        INSERT INTO t_user(username, password, phone)
        VALUES (#{user.username}, #{user.password}, #{user.phone})
    

5 在核心配置文件中注册

    

6 测试代码
public class TestMain {
    
    public static void main(String[] args) {
        InputStream resourceAsStream = TestMain.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession session = ssf.openSession(true);
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("admin");
        user.setPassword("123456");
        user.setPhone("123");
        mapper.insert(user);
    }
}
SSM整合

SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)

Spring IoC 配置

Spring mvc 配置

Spring-mybatis 配置

一 技术栈
  1. spring
  2. springmvc
  3. mybatis
  4. druid
  5. jackson/fastjson
二 SS整合 1 导入依赖

    org.springframework
    spring-webmvc
    ${spring.version}

2 配置文件

spring-context.xml



    

spring-mvc.xml



    
    

三 SM整合 1 导入相关依赖


    mysql
    mysql-connector-java
    8.0.26


    com.alibaba
    druid
    1.2.7


    org.mybatis
    mybatis
    3.5.7


    org.mybatis
    mybatis-spring
    2.0.6

2 配置文件

所有的配置文件最终都会放到同一个容器里面,就像很多独立的章节,最终都会变成一篇论文,可以相互连接的

  • 注册SQLSessionFactory对象
    • 扫描所有的mapper.xml文件
    • 配置数据库连接池
  • 注册扫描所有的接口
    • MapperScannerConfigurer
  • 连接池的配置

UserMapper.xml

所有数据层的接口实现类用xxMapper.xml文件来写





    
        insert into t_user(username,password) values (#{user.username},#{user.password})
    

mybatis-config.xml




    
        
    

spring-mybatis.xml

注册对象有三种方式,xml、注解、java config,由于mybatis是第三方框架,使用注解难以注册,所以使用xml方式,mybatis就是对传统的JDBC进行封装2,所以就需要数据库连接池、加载mapper.xml文件里面的sql语句,结果集等等




    
        
        
        
       
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
            
                
            
        
    
    
    
    
    
        
        
        
        
        
        
    

spring-durid.xml

多个地方使用连接池,就需要单独配成一个配置文件




    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

常见错误

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.mybatis.spring.mapper.MapperScannerConfigurer#0' defined in file [D:workIdeaProjects2105ssm-exampletargetssm-exmapleWEB-INFclassesspring-mybatis.xml]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/dao/support/DaoSupport

解决:导入spring-orm依赖


      org.springframework
      spring-orm
      5.3.9

四 web.xml


    Archetype Created Web Application
    
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
     
    
        contextConfigLocation
        classpath:spring-*.xml
    
    
    
    
        dispatcherServlet
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextConfigLocation
            classpath:spring-mvc.xml
        
    
    
      
    
        dispatcherServlet
        /
    

XML映射器 一 概要
  1. mapper.xml文件(标签)

    1. 增删改

    2. 查

      多表查询(join)

    3. 结果集标签

  2. 动态SQL

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

核心顶级标签

  • insert – 映射插入语句。

  • update – 映射更新语句。

  • delete – 映射删除语句。

  • select – 映射查询语句。

  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。

其他

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • sql – 可被其它语句引用的可重用语句块。
二 查询(单表)

通过select标签来实现

常用属性
属性说明
id对应接口的名称
parameterType推荐使用@Param来传递参数 (开发中能不用就不用)
resultType返回查询时候指定的类型(开发中建议不要使用)
resultMap配合resultMap标签使用 跟resultType属性只能选其一
例子 1 mapper接口
public interface UserMapper {
    
    User selectById(@Param("uid") Long uid);
    
    List selectList(@Param("limit") int limit, @Param("offset") int offset);
}

2 mapper映射
    

        SELECT u.uid, u.username, u.password, u.phone, u.create_date, u.status
        FROM t_user u
        WHERe status = 1
        LIMIT #{limit} , #{offset}
    
3 resultMap


    
    
    



    
    
    
    
    


4 单元测试
@SpringJUnitConfig(
        locations =
                {"classpath:spring-context.xml",
                        "classpath:spring-mvc.xml",
                        "classpath:spring-mybatis.xml",
                        "classpath:spring-druid.xml"})
public class UserMapperTest {
    @Resource
    UserMapper userMapper;

    @Test
    void selectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user.toString());
    }

    @Test
    void selectList() {
        List users = userMapper.selectList(0, 10);
        users.forEach(user -> System.out.println(user.getUsername()));
    }
}
三 resultMap 概要

定义结果集映射

  1. 属性跟列名映射
属性
属性说明
id唯一标识
typeEntity类型 映射结果集的类型
extends继承其他的resultMap
子标签
标签名说明
id定义主键
result定义普通的字段与属性的对应关系
四 增删改 @Builder

@Builder,有@Builder要添加@NoArgsConstructor和@AllArgsConstructor,否则有可能映射不到构造方法

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    
    private Long uid;

    
    private String username;

    
    private String password;

    
    private String phone;
}
public class UserMapperTest {
    @Resource
    UserMapper userMapper;

    @Test
    void insert() {
        User user = User.builder()
                .username("test11111")
                .password("123456")
                .phone("123132132")
                .build();
        userMapper.insert(user);
        System.out.println(user.getUid());

    }
}
BeanUtils.copyProperties

使用BeanUtils.copyProperties就不需要其他注解了

@Data
public class Access {
    
    private Long accessId;

    
    private String accessNum;

    
    private String accessName;

    
    private String accessType;

    
    private String accessSite;

    
    private String direction;

    
    private Date createDate;

    
    private String description;

    
    private Boolean status;
}

使用BeanUtils.copyProperties(param,access)赋值

@Service
public class AccessServiceImpl implements AccessService {
    @Resource
    private AccessMapper accessMapper;

    
    @Override
    public Boolean insert(AccessRequestParam param) {
        try {
            Access access = new Access();
            BeanUtils.copyProperties(param,access);
            Integer insert = accessMapper.insert(access);
            return insert>0;
        } catch (BeansException e) {
            throw new ServiceException(ResponseCode.SYS_ERROR);
        }
    }
    
    @Override
    public Boolean update(AccessRequestParam param) {
        try {
            Access access = new Access();
            BeanUtils.copyProperties(param,access);
            Integer update = accessMapper.update(access);
            return update>0;
        } catch (BeansException e) {
            throw new ServiceException(ResponseCode.SYS_ERROR);
        }
    }
}
insert 作用

用户往数据中插入数据

属性
属性说明
id对应接口方法的名字,如id=“insert”
useGeneratedKeys是否获取数据库自增主键,取值为true或false,如useGeneratedKeys=“true”
keyProperty赋值主键值的字段,如keyProperty=“user.uid”
例子

mapper接口

public interface UserMapper {
    int insert(@Param("user") User user);
}

mapper.xml


    INSERT INTO t_user (username, password, phone)
    VALUES (#{user.username}, #{user.password}, #{user.phone})

备注:
  • 获取保存数据的主键的值的时候

例子

mapper接口

 int insert(@Param("user") User user);

mapper.xml

   
        INSERT INTO t_user (username, password, phone)
        VALUES (#{user.username}, #{user.password}, #{user.phone})
    

单元测试

@Test
void insert() {
    User user = User.builder()
        .username("test11111")
        .password("123456")
        .phone("123132132")
        .build();
    userMapper.insert(user);
    System.out.println(user.getUid());
}

update/delete 作用

更新和删除数据

属性
属性说明
id对应接口方法的名字

mapper接口

int delete(@Param("id") Long uid);


int update(@Param("user") User user);

mapper.xml


    UPDATE t_user u
    SET u.username=#{user.username},
    u.password=#{user.password},
    u.phone= #{user.phone},
    u.status  = #{user.status}
    WHERe u.uid = #{user.uid}



    UPDATE t_user u
    SET u.status = 0
    WHERe u.uid = #{id}

测试代码

@Test
void delete() {
    int delete = userMapper.delete(13L);
    if (delete > 0) {
        System.out.println("删除成功");
    } else {
        System.out.println("删除失败");
    }
}
五 单元测试junit5 导入依赖


    org.junit.jupiter
    junit-jupiter-api
    5.8.1
    test



    org.springframework
    spring-test
    5.3.9
    test



    javax.servlet
    javax.servlet-api
    4.0.1
    provided

测试数据层
public interface UserMapper {
    
    User selectById(@Param("uid") Long uid);
}



    
        
        
        
    
	
    
        
        
        
        
        
    


    
    SELECT u.uid,
    u.username,
    u.password,
    u.phone,
    u.create_date,
    u.status,
    ud.user_detail_id,
    ud.email,
    ud.status,
    ud.uid
    FROM t_user u
    LEFT JOIN t_user_detail ud ON u.uid = ud.uid
    WHERe u.username = #{username}

测试代码
 @Test
 void selectUserByName() {
     User user = userMapper.selectByUsername("霞霞1号");
     System.out.println(user.getUserDetail().getEmail());
}
四 一对多 1 概要

一方持有多方的引用

如:

用户有多个订单 ,一个订单有多个商品

一级菜单包含多个二级菜单

2 SQL(关系模型)

用户表

CREATE TABLE t_user
(
    uid         bigint AUTO_INCREMENT       PRIMARY KEY,
    username    varchar(128)                       NOT NULL COMMENT '用户名',
    password    varchar(128)                       NOT NULL COMMENT '密码',
    phone       varchar(11)                        NOT NULL COMMENT '手机号',
    create_date datetime DEFAULT CURRENT_TIMESTAMP NULL COMMENT '创建时间',
    status      int(1)   DEFAULT 1                 NOT NULL COMMENT '1 表示正常  0 表示删除',
    CONSTRAINT phone   UNIQUE (phone),
    CONSTRAINT username   UNIQUE (username)
);

订单表

CREATE TABLE t_order
(
    order_id bigint AUTO_INCREMENT
        PRIMARY KEY,
    order_no bigint           NOT NULL COMMENT '订单号',
    msg      varchar(255)     NULL COMMENT '附加信息',
    status   int(1) DEFAULT 1 NULL,
    uid      bigint           NOT NULL COMMENT '用户ID',
    CONSTRAINT order_no
        UNIQUE (order_no)
);
3 对象模型
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    
    private List orders;
}

@Data
public class Order {
    
    private Long orderId;

    
    private Long orderNo;

    
    private String msg;

    
    private Integer status;

    
    private Long uid;
}


4 mapper接口
User selectOrderById(@Param("uid") Long uid);
5 mapper.xml
    
    
    SELECT *
    FROM t_user
    WHERe uid = #{uid}
    

ResultMap

  • column 指定外键字段
  • select = 查询多方的接口

    
    
    
    
    
    
    
    
    

订单相关

OrderMapper 接口

//两个接口都通过uid来查询,只是OrderMapper接口通过selectOrders方法,通过uid查询到的用户的所有订单封装到List传给User对象
public interface OrderMapper {
    List selectOrders(@Param("uid") Long uid);
}

OrderMapper.xml




    
        SELECT r.role_id,
               r.name        role_name,
               r.role_dec,
               r.create_date role_create_date,
               r.status      role_status,
               p.per_id,
               p.name,
               p.per_dec,
               p.create_date,
               p.status
        FROM t_role r
                     LEFT JOIN role_permission_relation rpr ON r.role_id = rpr.role_id
                     LEFT JOIN t_permission p ON p.per_id = rpr.per_id
        WHERe r.status = 1
          AND p.status = 1
    




    
        
        
        
    

六 注意
  • 多表查询的时候如果查询的表的字段出现名字相同的时候,需要使用别名来解决数据错误的情况
动态SQL 1 if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:


  SELECT * FROM BLOG WHERe state = ‘ACTIVE’
  
    AND title like #{title}
  
  
    AND author_name like #{author.name}
  

2 choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。


  SELECT * FROM BLOG
  WHERe
  
    state = #{state}
  
  
    AND title like #{title}
  
  
    AND author_name like #{author.name}
  

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERe

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECt * FROM BLOG
WHERe
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:


  SELECT * FROM BLOG
  
    
         state = #{state}
    
    
        AND title like #{title}
    
    
        AND author_name like #{author.name}
    
  

where 元素只会在子元素返回任何内容的情况下才插入 “WHERe” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除


  SELECT *
  FROM POST P
  WHERe ID in
  
        #{item}
  

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

事务 一 Spring 事务

用户定义一系列SQL操作的序列,这些做操作要么一起执行成功要么一起执行失败

  • 包含多条SQL语句
  • 多条SQL语句中包含,增删改的操作
  • 如果多条SQL语句都是查询的操作,可以不需要开启事务
特性
  • 原子性(Atomic)

  • 一致性(Consistent)

  • 隔离性(Isolated)

    • 读取未提交 未提交读(readuncommited)

    • 读取已提交(针对更新操作) 提交读(readcommit)

    • 可重复读(增加删除 多条记录) 重复读(repeatableread)

    • 串行化(serializable)

  • 持久性(durable)

二 概要

Spring对事务也进行了支持 底层AOP实现

分类:

  1. 编程事务 (不推荐)
  2. 声明事务 (注解+配置)
三 基础使用 导入相关依赖

  org.springframework
  spring-tx
  5.3.9



  javax.servlet
  javax.servlet-api
  4.0.1



  cn.hutool
  hutool-all
  5.7.9

配置


    
  
  
  

业务层 @Transactional
@Service
public class OrderServiceImpl implements OrderService {
  // 开启事务    @Transactional
  @Transactional
  @Override
  public String createOrder() {
      return null;
  }
}
四 核心注解 作用

@Transactional 开启 Spring 事务支持

源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
	Propagation propagation() default Propagation.REQUIRED;
	Isolation isolation() default Isolation.DEFAULT;//设置隔离级别
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
	String timeoutString() default "";
	boolean readOnly() default false;
	Class[] rollbackFor() default {};
}
属性说明
Isolation设置隔离(在开发中不要去设置隔离级别) mysql隔离 REPEATABLE_READ oracle READ_COMMITTED
propagation事务传播机制
timeout事务的超时时间 默认 -1 推荐 3 单位是秒
readOnly表示可读性事务(建议不要设置)
rollbackFor默认 RuntimeException 级别
propagation

事务的传播机制

@Transactional(propagation=Propagation.REQUIRED)

可选值

可选值说明
REQUIRED如果当前方法有事务就加入事务,没有就开启事务
SUPPORTS如果入口方法有事务,就加入事务,如果入口方法没事务则不开启事务
MANDATORY入口方法必须有事务,如果没有事务直接抛异常
PROPAGATION_REQUIRES_NEW不管入口方法有没有事务都单独开启事务
NOT_SUPPORTED不开启事务
NEVER必须在一个没有事务的方法中执行,否则直接抛异常
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作
五 事务失效场景
  • 在业务层的实现类上使用(类上加注解)
  1. 方法必须是public声明
  • AOP --> 反射 —> 获取所有公开的方法;
  1. 异常必须是RuntimException异常或者子类

  2. springmvc 重复扫描也会导致事务失效

  3. 数据必须支持事务 innoDB引擎才支持事务 MyISAM引擎不支持事务

  4. 异常被你处理

六 如何使用 注册送积分

注册送积分

  1. 查询用户是否存在
  2. 保存用户信息
  3. 添加积分
用户表
create table member(
  member_id bigint auto_increment primary key ,
    
  username varchar(255) unique not null comment '用户名',
  password varchar(128) not null comment '密码',
    
  phone varchar(11) unique not null comment '手机号',
    
  email varchar(255) unique not null comment '邮箱',
  create_date datetime default now() not null comment '注册时间',
    
  update_date datetime default now() on update now() not null comment '最后更新时间',
  status tinyint(1) default 1 not null comment '是否删除 1 正常 0删除',
  head varchar(255) default '/static/image/default.png' not null comment '用户头像'
);
积分表
create table t_integral
(
    integral_id    int auto_increment primary key comment '主键',
    type           int                    not null comment '积分类型 1 表示新用户注册送积分 2 签到送积分',
    score          int                    not null comment '积分',
    create_date    datetime default now() not null comment '创建时间',
    operation_type int                    not null comment '1表示添加积分 2表示消费积分',
    expire_data    datetime               not null comment '积分过期时间',
    member_id      bigint                 not null comment '用户id',
    status         tinyint  default 1     not null comment '1 正常 2 过期 3冻结'
);
MemberMapper.xml



    
        
        
        
        
        
        
    

    
        select a.apply_id, a.username, a.key_type, a.phone, a.id_card, a.key_password from rp_key_apply a
        where status=1
            
                and a.username like concat('%',#{name},'%')
            
            
                and a.key_type like concat('%',#{type},'%')
            
            
                and a.phone like concat('%',#{phone},'%')
            
    

如果status是多个状态,不只是删除和正常的状态,即status也是动态的,就用where标签解决


        SELECT c.cart_id,
               c.product_id,
               c.member_id,
               c.quantity,
               c.product_name,
               c.product_price
        FROM t_carts c
        WHERe member_id = #{memberId}
          AND product_id = #{productId}
          AND status = 1
    

    
        INSERT INTO t_carts
        
            
                member_id,
            
            
                product_id,
            
            
                product_name,
            
            
                quantity,
            
            
                product_price,
            
        
        
            
                #{carts.memberId},
            
            
                #{carts.productId},
            
            
                #{carts.productName},
            
            
                #{carts.quantity},
            
            
                #{carts.productPrice},
            
        
    

    
        UPDATE t_carts
        
            
                product_name= #{carts.productName},
            
            
                product_price= #{carts.productPrice},
            
            
                quantity= #{carts.quantity},
            
            
                status=#{carts.status},
            
            
                member_id=#{carts.memberId},
            
            
                product_id =#{carts.productId},
            
        
        WHERe cart_id = #{carts.cartId}
    

    
    
        
        UPDATE t_carts
        SET status=0
                WHERe member_id = #{memberId}
                  AND status = 1
                  AND cart_id IN
        
            #{id}
        
    

CartMapper
public interface CartMapper {
    
    Carts selectCartsByMemberId(@Param("memberId") Long memberId, @Param("productId") Long productId);

    
    int insert(@Param("carts") Carts carts);

    
    int updateById(@Param("carts") Carts carts);

    批量删除
    int batch(@Param("ids") List ids, @Param("memberId") Long memberId);
}
CartService
public interface CartService {
    Boolean add(CartsRequestParams cartsRequestParams);

    int batchDel(List ids, Long memberId);
}
CartServiceImpl
@Service
public class CartServiceImpl implements CartService {
    @Resource
    CartMapper cartMapper;

    
    @Override
    public Boolean add(CartsRequestParams cartsRequestParams) {
        int count;
        try {
            
            Carts carts = cartMapper.selectCartsByMemberId(cartsRequestParams.getMemberId(), cartsRequestParams.getProductId());
            if (carts == null) {
                //添加
                carts = new Carts();
//            Spring BeanUtils
                BeanUtils.copyProperties(cartsRequestParams, carts);
                count = cartMapper.insert(carts);
            } else {
                //  更新的操作   数量的计算  >1
                int quantity = cartsRequestParams.getQuantity() > 1 ? cartsRequestParams.getQuantity() : 1;
                carts.setQuantity(carts.getQuantity() + quantity);
                count = cartMapper.updateById(carts);
            }
        } catch (Exception ex) {
            throw new ServiceException(ResponseCode.SYS_ERROR);
        }
        return count > 0;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public int batchDel(List ids, Long memberId) {
        int batch = cartMapper.batch(ids, memberId);
        if (batch == ids.size()) {
            return batch;
        } else {
            throw new ServiceException(ResponseCode.ERROR);
        }

    }
}
CartController
@RestController
@RequestMapping("/carts")
public class CartController {
    
    @Resource
    private CartService cartService;

    
    @PostMapping("/add")
    public ResponseResult add(@RequestBody CartsRequestParams cartsRequestParams) {
        Boolean isAdd = cartService.add(cartsRequestParams);
        return ResponseResult.success(isAdd);
    }

    
    @PostMapping("/del/batch/")
    public ResponseResult batchDel(@RequestParam List ids, @RequestParam Long memberId) {
        return ResponseResult.success(cartService.batchDel(ids, memberId));
    }
}
全局异常处理

异常处理范例

@Service
public class MemberServiceImpl implements MemberService {
    @Override
    public String register(String username, String password) {
        // 第一步  判断用户名或者手机号
        if (false) {
            // 注册逻辑代码
        } else {
            throw new ServiceException(ResponseCode.ACCOUNT_IS_EXIST);
        }
        return null;
    }
}
枚举类响应状态码
@Getter
public enum ResponseCode {
    
    SUCCESS(200, "success"),
    ERROR(404, "error"),
    SYS_ERROR(40400, "sys error"),
    

    
//    用户状态码
    ACCOUNT_IS_EXIST(40100, "username  or  phone  is exist");

    private int status;
    private String msg;

    ResponseCode(int status, String msg) {
        this.status = status;
        this.msg = msg;
    }
}
统一结果集
@Data
@AllArgsConstructor//提供全参构造方法
@NoArgsConstructor//提供无参构造方法
@Builder
public class ResponseResult {
    private int status;
    private String msg;
    private T data;


    
    public static  ResponseResult success(T data) {
        return success(data, ResponseCode.SUCCESS);
    }

    //泛型 静态方法前需要加
    public static  ResponseResult success(T data, ResponseCode responseCode) {
        return new ResponseResultBuilder()
                .msg(responseCode.getMsg())
                .status(responseCode.getStatus())
                .data(data)
                .build();
    }


    public static  ResponseResult error() {
        return error(ResponseCode.ERROR);
    }

    public static  ResponseResult error(ResponseCode responseCode) {
        return error(responseCode.getStatus(), responseCode.getMsg());
    }

    public static  ResponseResult error(int status, String msg) {
        return new ResponseResultBuilder()
                .msg(msg)
                .status(status)
                .build();
    }

}
无语义异常处理
@Getter
public class BaseException extends RuntimeException {
    private ResponseCode responseCode;

    
    public BaseException(ResponseCode responseCode) {
        super(responseCode.getMsg());
        this.responseCode = responseCode;
    }
}
控制层异常处理,继承BaseException无语义的异常处理
public class ControllerException extends BaseException {
    public ControllerException(ResponseCode responseCode) {
        super(responseCode);
    }
}
业务层异常处理,继承BaseException无语义的异常处理
public class ServiceException extends BaseException {
    public ServiceException(ResponseCode responseCode) {
        super(responseCode);
    }
}
全局异常处理类
@RestControllerAdvice
@Slf4j
public class GlobalHandlerException {

    
    @ExceptionHandler(Exception.class)
    public static ResponseResult handlerException(Exception ex) {
        //打印异常信息,不然不会报错,也不知道是什么错误
        log.error(ex.getMessage());
        //如果是业务层的异常
        if (ex instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) ex;
            return ResponseResult.error(serviceException.getResponseCode());
        }
        return ResponseResult.error();
    }

    //只接收业务层的异常ServiceException.class,当然也可以定义一个只接收控制层或者其他层的异常
    @ExceptionHandler(ServiceException.class)
    public static ResponseResult handlerException(ServiceException ex) {
        log.error(ex.getMessage());
        return ResponseResult.error(ex.getResponseCode());
    }

}
 
单表一对多 
两级菜单 
entity 
@Data
public class Category {
    private Integer cateId;

    
    private String name;

    
    private String icon;

    
    private String url;

    
    private Date createDate;

    
    private Integer level;

    
    private Integer parentId;

    
    private Boolean status;

    //自己加
    private List subList;
}
CategoryMapper
public interface CategoryMapper {
    List selectList();
}
CategoryMapper.xml



    
    
        
        
        
        
        
        
        
        
    
    
    
    
        
    

    
    
        
        
        
        
        
        
        
        
    

    
        SELECT a.address_id,
               a.`name`,
               a.`status`,
               a2.address_id second_id,
               a2.`name`     second_name,
               a2.`status`   second_status,
               a3.address_id third_id,
               a3.`name`     third_name,
               a3.status     third_status,
               a4.address_id fourth_id,
               a4.`name`     fourth_name,
               a4.status     fourth_status
        FROM address a
                     LEFT JOIN address a2 ON a.address_id = a2.parent_id
                     LEFT JOIN address a3 ON a2.`address_id` = a3.`parent_id`
                     LEFT JOIN address a4 ON a3.`address_id` = a4.`parent_id`
        WHERe a.`parent_id` = 0;
    

result column=“sub_create_date” jdbcType=“TIMESTAMP” property=“createDate”/>




        SELECT a.address_id,
               a.`name`,
               a.`status`,
               a2.address_id second_id,
               a2.`name`     second_name,
               a2.`status`   second_status,
               a3.address_id third_id,
               a3.`name`     third_name,
               a3.status     third_status,
               a4.address_id fourth_id,
               a4.`name`     fourth_name,
               a4.status     fourth_status
        FROM address a
                     LEFT JOIN address a2 ON a.address_id = a2.parent_id
                     LEFT JOIN address a3 ON a2.`address_id` = a3.`parent_id`
                     LEFT JOIN address a4 ON a3.`address_id` = a4.`parent_id`
        WHERe a.`parent_id` = 0;
    

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号