1.pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.16
com.example
StudyMybatisPlus
0.0.1-SNAPSHOT
StudyMybatisPlus
Demo project for Spring Boot
11
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
com.baomidou
mybatis-plus-boot-starter
3.5.1
mysql
mysql-connector-java
8.0.21
org.projectlombok
lombok
1.18.12
org.reflections
reflections
0.9.10
org.springframework.boot
spring-boot-maven-plugin
2.application.yml
#配置端口
server:
port: 80
spring:
#配置数据源
datasource:
#配置数据源类型
type: com.zaxxer.hikari.HikariDataSource
#配置连接数据库的信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT
username: root
password: root
#mybatis plus配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 字段名和数据库中字段名一致
map-underscore-to-camel-case: false
global-config:
db-config:
#配置mybatis plus 在更新时只更新非空和非NULL的字段
update-strategy: not_empty
# 实体名字和数据库表名一致
table-underline: false
# 需要转化为json的字段
map-field-scan-package: "com.example"
3.MapData.java
package com.example.studymybatisplus.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MapData {
}
4.TypeConfig.java
package com.example.studymybatisplus.config;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.example.studymybatisplus.anno.MapData;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Field;
import java.util.Set;
/**
* 注册需要转换为Map的处理器
*/
@Configuration
public class TypeConfig implements MybatisPlusPropertiesCustomizer {
@Value("${map-field-scan-package}")
String packageName;
@Override
public void customize(MybatisPlusProperties properties) {
Reflections reflections = new Reflections(this.packageName);
Set> typesAnnotatedWith = reflections.getTypesAnnotatedWith(TableName.class);
typesAnnotatedWith.forEach(clazz -> {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getAnnotation(MapData.class) != null) {
properties.getConfiguration().getTypeHandlerRegistry().register(field.getType(), JacksonTypeHandler.class);
}
}
});
}
}
5.UserMapper.java
package com.example.studymybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.studymybatisplus.pojo.User;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserMapper extends BaseMapper {
/**
* 测试自定义sql
*/
List getUserList();
}
6.User.java
package com.example.studymybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.example.studymybatisplus.anno.MapData;
import lombok.Data;
@Data
@TableName(autoResultMap = true)
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name = "";
private Integer age = 0;
@MapData
private UserInfo info = new UserInfo();
}
7.UserInfo.java
package com.example.studymybatisplus.pojo;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class UserInfo {
private String address="";
private Map map = new HashMap<>();
private DataVo dataVo = new DataVo();
private Integer aaa;
}
8.DataVo.java
package com.example.studymybatisplus.pojo;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class DataVo {
private Integer num;
private Map map2 = new HashMap<>();
}
9.主方法
package com.example.studymybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.studymybatisplus.mapper")
public class StudyMybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(StudyMybatisPlusApplication.class, args);
}
}
10.UserMapper.xml // 测试自定义sql
可见,完全不需要ResultMap了,非常完美!!!
11.测试用例
package com.example.studymybatisplus;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.studymybatisplus.mapper.UserMapper;
import com.example.studymybatisplus.pojo.User;
import com.example.studymybatisplus.pojo.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Random;
@SpringBootTest
class StudyMybatisPlusApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
for (int i = 0; i < 10; i++) {
User newUser = new User();
newUser.setName("xx");
newUser.setAge(30);
newUser.getInfo().setAddress("北京 " + new Random().nextInt(10000));
newUser.getInfo().getMap().put(1, 123);
newUser.getInfo().getDataVo().setNum(222);
newUser.getInfo().getDataVo().getMap2().put(666, 888);
newUser.getInfo().getDataVo2().setNum(112222);
int insert = userMapper.insert(newUser);
System.out.println("insert:" + insert);
System.out.println(newUser);
// 测试QueryWrapper
LambdaQueryWrapper lambda = new QueryWrapper().lambda();
List userList1 = userMapper.selectList(lambda);
System.out.println(userList1);
// 现在自定义sql也完全不需要ResultMap了
List userList2 = userMapper.getUserList();
System.out.println(userList2);
}
}
}
9.输出
JDBC Connection [HikariProxyConnection@1111497601 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d1463] will not be managed by Spring
==> Preparing: SELECT id,name,age,info FROM user
==> Parameters:
<== Columns: id, name, age, info
<== Row: 1, xx, 30, <>
<== Row: 2, xx, 30, <>
<== Row: 3, xx, 30, <>
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27502e5c]
[User(id=1, name=xx, age=30, info=UserInfo(address=北京2, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null)), User(id=2, name=xx, age=30, info=UserInfo(address=北京1, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null)), User(id=3, name=xx, age=30, info=UserInfo(address=北京 5542, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null))]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@12266084] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1839613624 wrapping com.mysql.cj.jdbc.ConnectionImpl@f1d1463] will not be managed by Spring
==> Preparing: SELECT id, name, age, info FROM user
==> Parameters:
<== Columns: id, name, age, info
<== Row: 1, xx, 30, <>
<== Row: 2, xx, 30, <>
<== Row: 3, xx, 30, <>
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@12266084]
[User(id=1, name=xx, age=30, info=UserInfo(address=北京2, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null)), User(id=2, name=xx, age=30, info=UserInfo(address=北京1, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null)), User(id=3, name=xx, age=30, info=UserInfo(address=北京 5542, map={1=123}, dataVo=DataVo(num=222, map2={666=888}), aaa=null))]
2023-10-22 00:14:07.258 INFO 10232 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-10-22 00:14:07.274 INFO 10232 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
可见,业务层可以愉快的使用Entity了,完全不需要关心是不是自定义sql,完美支持json,这样子mysql和mongodb就是一模一样了,只不过是复杂的类型多了一个自定义的@MapData注解!!
总结:
1.增加字段发现确实是可以的,删除字段就报错,所以这也符合游戏的目标也就是不能删和改字段名字。
2.注意在Entity中给默认值,因为我们用的都是包装类型,使用xdb的经验告诉我,所有的数据要给默认值,Map类型也要初始化一下。