Spring Boot 摸索之路-2:MyBatis-Plus

从JPAController到MyBatis-Plus CRUD(增删改查)
从Hibernate到MyBatis

对数据库的增删改查操作

sql语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--增
INSERT INTO table_name
VALUES (value1,value2,value3,...);

--删
DELETE FROM table_name
WHERE some_column=some_value;

--改
UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

--查
SELECT * FROM table_name;

这篇博客中的代码环境

  • 准备MySql数据库,建立数据表user

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE user
    (
    user_id BIGINT(20) NOT NULL COMMENT 'ID',
    user_name VARCHAR(30) NULL DEFAULT NULL COMMENT 'name',
    user_age INT(11) NULL DEFAULT NULL COMMENT 'age',
    PRIMARY KEY (user_id)
    ) charset = utf8;

一、传统Java开发中

1、在没有框架封装这些sql语句的时候

  1. 在Service层中写CRUD的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    protected EntityManager em;

    /**
    * 查找所有用户
    */
    public List<Module> findAllUser(){
    TypedQuery<Module> q =
    em.createQuery("SELECT u FROM User u", User.class);
    return q.getResultList();
    }
  2. 在业务代码中调用

    1
    2
    3
    4
    5
    6
    7
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("UserPU");
    EntityManager em = emf.createEntityManager();
    UserService userService = new UserServiceImpl(em);

    //调用CRUD方法
    List<User> userList = userService.findAllUser();
    userList..forEach(System.out::println);

2、在JPA通过数据库生成代码之后

对于简单的CRUD操作

只需要通过数据库来自动生成JPAController直接调用JPAController就可以完成简单的CRUD操作

如果需要自定义操作

  1. 我们需要在实体类中先来@NamedQuery

    1
    2
    3
    @NamedQueries({
    @NamedQuery(name = "User.findUserByName", query = "SELECT u FROM user u WHERE name like ?")
    })
  2. 然后在Service层中实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public Module findUserByName(String xxx){
    EntityManager em = getEntityManager();
    try{
    return em.createNamedQuery("User.findUserByName",
    User.class).setParameter("name",xxx).getSingleResult();
    } finally {
    em.close();
    }
    }
  3. 最后在业务代码中new一个UserJPAController的对象就可以调用方法了

    1
    2
    3
    4
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("UserPU");
    UserJPAController userJPAController = new UserJPAController(emf);

    userJPAController.findUserByName("Tony");

二、使用了Spring-Boot框架和MyBatis-Plus工具之后

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatis-Plus官方文档

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

支持数据库支持数据库

  • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver
  • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

使用MyBatis-Plus的自动生成工具生成EntityMapperServiceController

创建MyBatis-Plus Generator工具类MyBatisGenerator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class MyBatisGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(tip)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}

public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();


// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("YourName");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);

// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("DatabasePath");
// dsc.setSchemaName("public");
dsc.setDriverName("");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);

// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("com.company");
mpg.setPackageInfo(pc);

// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};

// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";

// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录");
return false;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);

// 配置模板
TemplateConfig templateConfig = new TemplateConfig();

// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();

templateConfig.setXml(null);
mpg.setTemplate(templateConfig);

// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}

}
  • 如果Import的时候发现GlobalConfig中的set方法找不到们可以去Import中删除

    1
    import com.baomidou.mybatisplus.core.config.GlobalConfig;

    运行MyBatisGenerator

    1. 在命令行输入需要自动生成的模块路径
    2. 第二行输入数据库的表名,多个字段用逗号隔开。

编写MyBatis配置类 MyBatisConfig

1
2
3
4
5
@Configuration
//MapperScan Mapper类的路径
@MapperScan("com.company")
public class MyBatisConfig {
}

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserServiceImpl {

@Autowired
private UserMapper userMapper;

/**
*
*/
@Test
public void testSelectAllUser(){
System.out.println("----------Select All-------------");
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
}

最后记录个小bug

  1. Bug Description

    在测试的代码中@AutowiredMapper类时,经常会遇到Could not autowire. No beans of 'xxxx' type found的错误提示。但程序的编译和运行都是没有问题的,这个错误提示并不会产生影响。但红色的错误提示在有些有强迫症的程序员眼里,多多少少有些不太舒服。

    Error

  1. Cause

    原因可能有两个,第一个是IntellijIDEA本身工具的问题。第二个便是我们导入@Service包的时候导入包错误造成的

      第一种原因,spring auto scan配置,在编辑情况下,无法找不到对应的bean,于是提示找不到对应bean的错误。常见于mybatis的mapper,如下:

    1
    2
    3
    4
    5
    <!-- mapper scanner configurer -->
    <bean id="mapperScannerConfig" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.adu.spring_test.mybatis.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>
  2. Solution

    针对第一种原因,解决办法是:降低Autowired检测的级别,将Severity的级别由之前的error改成warning或其它可以忽略的级别。

Setting

针对第二种原因,解决方案当然是导入正确的包。首先我们来看下最容易导入的错误包,如下所示:

1
2
3
4
import com.alibaba.dubbo.config.annotation.Service;

//正确的包应该是下面这个
import org.springframework.stereotype.Service;

切记切记啊!!!!


Reference
Allwayz