diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..21cfbda --- /dev/null +++ b/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.example + mybaits-issue-2956 + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-jdbc + 2.7.15 + + + + + org.springframework.boot + spring-boot-starter-test + 2.7.15 + test + + + + org.springframework.boot + spring-boot-starter-aop + 2.7.15 + + + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.3.0 + + + + + mysql + mysql-connector-java + 8.0.33 + + + + org.projectlombok + lombok + 1.18.26 + provided + + + + junit + junit + 4.12 + test + + + \ No newline at end of file diff --git a/src/main/java/org/example/Application.java b/src/main/java/org/example/Application.java new file mode 100644 index 0000000..1c6f53c --- /dev/null +++ b/src/main/java/org/example/Application.java @@ -0,0 +1,18 @@ +package org.example; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@EnableTransactionManagement +@EnableAspectJAutoProxy +@MapperScan(basePackages = "org.example.mapper") +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/entity/User.java b/src/main/java/org/example/entity/User.java new file mode 100644 index 0000000..01ca786 --- /dev/null +++ b/src/main/java/org/example/entity/User.java @@ -0,0 +1,13 @@ +package org.example.entity; + +import lombok.Getter; +import lombok.Setter; +import org.example.enums.GcType; + +@Getter +@Setter +public class User { + private Long id; + private String uu; + private GcType type; +} diff --git a/src/main/java/org/example/enums/GcType.java b/src/main/java/org/example/enums/GcType.java new file mode 100644 index 0000000..a73017c --- /dev/null +++ b/src/main/java/org/example/enums/GcType.java @@ -0,0 +1,32 @@ +package org.example.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum GcType { + G1(1), + ZGC(2) { + @Override + public String getName() { + return "zgc"; + } + }, + ; + + public String getName() { + return this.name(); + } + + private int value; + + public static GcType fromValue(int i) { + for (GcType t : values()) { + if (t.value == i) { + return t; + } + } + return null; + } +} diff --git a/src/main/java/org/example/mapper/UserMapper.java b/src/main/java/org/example/mapper/UserMapper.java new file mode 100644 index 0000000..227ee2f --- /dev/null +++ b/src/main/java/org/example/mapper/UserMapper.java @@ -0,0 +1,28 @@ +package org.example.mapper; + +import lombok.Getter; +import lombok.Setter; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.example.entity.User; +import org.example.enums.GcType; + +@Mapper +public interface UserMapper { + @Options(useGeneratedKeys = true, keyProperty = "id") + @Insert("insert into user (uu, `type`) values (#{user.uu}, #{user.type})") + int insert(@Param("user") User user); + + @Select("select * from user where `type`=#{req.gcType}") + User selectByRequestParam(@Param("req") RequestParam req); + + + @Getter + @Setter + public static class RequestParam { + private GcType gcType; + } +} diff --git a/src/main/java/org/example/typehandler/CustomEnumTypeHandler.java b/src/main/java/org/example/typehandler/CustomEnumTypeHandler.java new file mode 100644 index 0000000..b717bb9 --- /dev/null +++ b/src/main/java/org/example/typehandler/CustomEnumTypeHandler.java @@ -0,0 +1,45 @@ +package org.example.typehandler; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedTypes; +import org.example.enums.GcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@MappedTypes(GcType.class) +public class CustomEnumTypeHandler extends BaseTypeHandler { + private Class type; + + public CustomEnumTypeHandler() {} + + public CustomEnumTypeHandler(Class type) { + if (type == null) { + throw new IllegalArgumentException("Type argument cannot be null"); + } + this.type = type; + } + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, GcType parameter, JdbcType jdbcType) throws SQLException { + ps.setObject(i, parameter.getValue()); + } + + @Override + public GcType getNullableResult(ResultSet rs, String columnName) throws SQLException { + return GcType.fromValue(rs.getInt(columnName)); + } + + @Override + public GcType getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return GcType.fromValue(rs.getInt(columnIndex)); + } + + @Override + public GcType getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return GcType.fromValue(cs.getInt(columnIndex)); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..ef064f2 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8 + driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + jdbc-url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8 + password: + username: + driver-class-name: com.mysql.cj.jdbc.Driver + +mybatis: + type-handlers-package: org.example.typehandler +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..7eed309 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,9 @@ + +CREATE TABLE `user` +( + `id` bigint NOT NULL AUTO_INCREMENT, + `uu` varchar(255) NOT NULL, + `type` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; \ No newline at end of file diff --git a/src/test/java/com/test/MybatisTest.java b/src/test/java/com/test/MybatisTest.java new file mode 100644 index 0000000..707db34 --- /dev/null +++ b/src/test/java/com/test/MybatisTest.java @@ -0,0 +1,59 @@ +package com.test; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.type.EnumTypeHandler; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; +import org.example.Application; +import org.example.entity.User; +import org.example.enums.GcType; +import org.example.mapper.UserMapper; +import org.example.mapper.UserMapper.RequestParam; +import org.example.typehandler.CustomEnumTypeHandler; +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 org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +@Transactional +public class MybatisTest { + @Autowired + private SqlSessionFactory sqlSessionFactory; + @Autowired + private UserMapper userMapper; + + @Test + public void testEnumTypeHandler() { + User user = new User(); + user.setUu("user1"); + user.setType(GcType.G1); + + // insert into user (uu, `type`) values ('user1', '1'); + userMapper.insert(user); + + TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry(); + + TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(GcType.class); + + // here + assertEquals(typeHandler.getClass(), CustomEnumTypeHandler.class); + + RequestParam req = new RequestParam(); + req.setGcType(GcType.ZGC); + + // actual: select * from user where `type` = 'ZGC'; + // expected: select * from user where `type` = 2; + userMapper.selectByRequestParam(req); + + TypeHandler typeHandler2 = typeHandlerRegistry.getTypeHandler(GcType.class); + + // here + assertEquals(typeHandler2.getClass(), EnumTypeHandler.class); + } +}