1.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.16</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>StudyMybatisPlus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>StudyMybatisPlus</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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&allowPublicKeyRetrieval=true
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.HashSet;
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<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(TableName.class);
Set<Class<?>> mapTypeSet = new HashSet<>();
typesAnnotatedWith.forEach(clazz -> {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getAnnotation(MapData.class) != null) {
if(mapTypeSet.contains(field.getType())){
continue;
}
mapTypeSet.add(field.getType());
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<User> {
/**
* 测试自定义sql
*/
List<User> 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<Integer, Integer> 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<Integer, Integer> 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
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.studymybatisplus.mapper.UserMapper">
<select id="getUserList" resultType="com.example.studymybatisplus.pojo.User">
SELECT id, name, age, info
FROM user
</select>
</mapper>
可见,完全不需要ResultMap了,非常完美!!!
11.测试用例
package com.example.studymybatisplus;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.studymybatisplus.mapper.UserMapper;
import com.example.studymybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Random;
@SpringBootTest
class StudyMybatisPlusApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void insertAndQuery() {
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
List<User> userList1 = userMapper.selectList(new QueryWrapper<User>().lambda());
System.out.println(userList1);
// 现在自定义sql也完全不需要ResultMap了
List<User> userList2 = userMapper.getUserList();
System.out.println(userList2);
}
}
@Test
void clearAllData() {
userMapper.delete(null);
}
@Test
void addOneUser() {
User user = new User();
Map<Integer, Integer> receiveState = user.getInfo().getReceiveState();
receiveState.put(1, 0);
receiveState.put(2, 1);
userMapper.insert(user);
System.out.println(user.getId());
}
@Test
void updateUser() {
User user = userMapper.selectById(58);
user.getInfo().getReceiveState().put(3, 3);
userMapper.updateById(user);
User user2 = userMapper.selectById(59);
user2.getInfo().getReceiveState().put(33, 33);
userMapper.updateById(user2);
}
}
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, <<BLOB>>
<== Row: 2, xx, 30, <<BLOB>>
<== Row: 3, xx, 30, <<BLOB>>
<== 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, <<BLOB>>
<== Row: 2, xx, 30, <<BLOB>>
<== Row: 3, xx, 30, <<BLOB>>
<== 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类型也要初始化一下。