├── ok.png ├── logo.png ├── logo-bak.png ├── mybatis.jpg ├── mybatis.png ├── mybatis-pro-boot-starter ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── boot │ │ └── starter │ │ ├── Empty.java │ │ └── package-info.java └── pom.xml ├── mybatis-pro-samples ├── spring-mvc │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dreamroute │ │ │ └── mybatis │ │ │ └── pro │ │ │ └── sample │ │ │ └── springmvc │ │ │ └── Empty.java │ └── pom.xml ├── spring-boot │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── github │ │ │ │ │ └── dreamroute │ │ │ │ │ └── mybatis │ │ │ │ │ └── pro │ │ │ │ │ └── sample │ │ │ │ │ └── springboot │ │ │ │ │ ├── c │ │ │ │ │ ├── d │ │ │ │ │ │ └── DD.java │ │ │ │ │ └── e │ │ │ │ │ │ └── x │ │ │ │ │ │ └── XX.java │ │ │ │ │ ├── domain │ │ │ │ │ ├── Demo.java │ │ │ │ │ ├── EnumTypeHandler.java │ │ │ │ │ ├── Gender.java │ │ │ │ │ ├── Dict.java │ │ │ │ │ └── User.java │ │ │ │ │ ├── a │ │ │ │ │ └── mapper │ │ │ │ │ │ └── Amapper.java │ │ │ │ │ ├── b │ │ │ │ │ └── mapper │ │ │ │ │ │ └── Bmapper.java │ │ │ │ │ ├── config │ │ │ │ │ ├── MapperConfig.java │ │ │ │ │ └── HttpMsgConverterConfig.java │ │ │ │ │ ├── mapper │ │ │ │ │ ├── EnumTypeHandlerMapper.java │ │ │ │ │ ├── DictMapper.java │ │ │ │ │ └── UserMapper.java │ │ │ │ │ ├── service │ │ │ │ │ ├── UserService.java │ │ │ │ │ ├── DictService.java │ │ │ │ │ └── impl │ │ │ │ │ │ ├── UserServiceImpl.java │ │ │ │ │ │ └── DictServiceImpl.java │ │ │ │ │ ├── util │ │ │ │ │ ├── anno │ │ │ │ │ │ └── Dict.java │ │ │ │ │ ├── DictFieldCache.java │ │ │ │ │ └── DictUtil.java │ │ │ │ │ ├── Application.java │ │ │ │ │ └── controller │ │ │ │ │ └── DemoController.java │ │ │ └── resources │ │ │ │ ├── application.properties │ │ │ │ ├── schema.sql │ │ │ │ └── mapper │ │ │ │ └── UserMapper.xml │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dreamroute │ │ │ └── mybatis │ │ │ └── pro │ │ │ └── sample │ │ │ └── springboot │ │ │ ├── interceptor │ │ │ ├── LogicalDeleteInterceptorTest.java │ │ │ ├── LockerInterceptorTest.java │ │ │ └── LimitColumnInterceptorTest.java │ │ │ ├── misc │ │ │ ├── ClassPathUtilTest.java │ │ │ └── UserMapperTest.java │ │ │ ├── condition │ │ │ ├── AliasTest.java │ │ │ ├── ExistByTest.java │ │ │ ├── DeleteByTest.java │ │ │ ├── CountByTest.java │ │ │ └── LimitTest.java │ │ │ ├── sdk │ │ │ ├── DeleteTest.java │ │ │ ├── UpdateTest.java │ │ │ ├── SelectTest.java │ │ │ └── InsertTest.java │ │ │ ├── mapper │ │ │ └── UserMapperTest.java │ │ │ ├── typehandler │ │ │ └── EnumTypeHandlerTest.java │ │ │ ├── controller │ │ │ └── DemoControllerTest.java │ │ │ └── service │ │ │ └── UserServiceTest.java │ └── pom.xml └── pom.xml ├── mybatis-pro-base ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dreamroute │ │ │ └── mybatis │ │ │ └── pro │ │ │ └── base │ │ │ ├── codec │ │ │ ├── date │ │ │ │ ├── package-info.java │ │ │ │ ├── LocalDateSerializer.java │ │ │ │ ├── DateSerializer.java │ │ │ │ ├── LocalDateTimeSerializer.java │ │ │ │ ├── DateDeserializer.java │ │ │ │ ├── LocalDateDeserializer.java │ │ │ │ └── LocalDateTimeDeserializer.java │ │ │ ├── enums │ │ │ │ ├── EnumMarkerSerializer.java │ │ │ │ ├── EnumMarker.java │ │ │ │ ├── EnumMarkerDeserializer.java │ │ │ │ ├── EnumMarkerSerializerForWeb.java │ │ │ │ ├── EnumMarkerSerializerForExtra.java │ │ │ │ ├── EnumMarkerSerializerForExtraCollection.java │ │ │ │ ├── EnumMarkerDeserializerForCollection.java │ │ │ │ ├── package-info.java │ │ │ │ └── JsonUtil.java │ │ │ └── PropertyAliasCache.java │ │ │ ├── typehandler │ │ │ └── EnumTypeHandler.java │ │ │ └── util │ │ │ └── ClassPathUtil.java │ └── test │ │ └── java │ │ └── com │ │ └── githu │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── base │ │ ├── User.java │ │ ├── MiscTest.java │ │ ├── Gender.java │ │ └── JsonUtilTest.java └── pom.xml ├── mybatis-pro-core ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dreamroute │ │ │ └── mybatis │ │ │ └── pro │ │ │ └── core │ │ │ ├── Demo.java │ │ │ ├── DemoMapper.java │ │ │ ├── EnumTest.java │ │ │ ├── MyBatisProUtilTest.java │ │ │ └── ClassUtilTest.java │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── core │ │ ├── annotations │ │ ├── Type.java │ │ ├── Transient.java │ │ ├── Column.java │ │ ├── Table.java │ │ └── Id.java │ │ ├── consts │ │ ├── ToLineThreadLocal.java │ │ ├── MyBatisProConfig.java │ │ ├── LogicalDeleteType.java │ │ ├── MapperLabel.java │ │ ├── KeyWord.java │ │ └── MyBatisProProperties.java │ │ ├── exception │ │ └── MyBatisProException.java │ │ └── util │ │ ├── XmlUtil.java │ │ ├── DocumentUtil.java │ │ └── ClassUtil.java └── pom.xml ├── mybatis-pro-auto-configuration ├── src │ └── main │ │ └── resources │ │ └── META-INF │ │ └── spring.factories └── pom.xml ├── mybatis-pro-sdk ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── sdk │ │ ├── Mapper.java │ │ ├── DeleteMapper.java │ │ ├── UpdateMapper.java │ │ ├── InsertMapper.java │ │ └── SelectMapper.java └── pom.xml ├── mybatis-pro-service ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── service │ │ ├── mapper │ │ └── BaseMapper.java │ │ ├── adaptor │ │ ├── id │ │ │ ├── Ids.java │ │ │ └── Id.java │ │ └── validator │ │ │ ├── impl │ │ │ ├── ElementNotEmptyValidator.java │ │ │ └── TimeValidator.java │ │ │ ├── ElementNotEmpty.java │ │ │ └── Time.java │ │ ├── entity │ │ ├── IdEntity.java │ │ └── BaseEntity.java │ │ └── service │ │ ├── BaseService.java │ │ └── AbstractServiceImpl.java └── pom.xml ├── .gitignore ├── mybatis-pro-interceptor ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dreamroute │ │ └── mybatis │ │ └── pro │ │ └── interceptor │ │ └── ProxyUtil.java └── pom.xml └── README.md /ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamroute/mybatis-pro/HEAD/ok.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamroute/mybatis-pro/HEAD/logo.png -------------------------------------------------------------------------------- /logo-bak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamroute/mybatis-pro/HEAD/logo-bak.png -------------------------------------------------------------------------------- /mybatis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamroute/mybatis-pro/HEAD/mybatis.jpg -------------------------------------------------------------------------------- /mybatis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamroute/mybatis-pro/HEAD/mybatis.png -------------------------------------------------------------------------------- /mybatis-pro-boot-starter/src/main/java/com/github/dreamroute/mybatis/pro/boot/starter/Empty.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.boot.starter; 2 | 3 | public class Empty {} 4 | -------------------------------------------------------------------------------- /mybatis-pro-boot-starter/src/main/java/com/github/dreamroute/mybatis/pro/boot/starter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * starter, 用于依赖相关组件 3 | */ 4 | package com.github.dreamroute.mybatis.pro.boot.starter; -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-mvc/src/main/java/com/github/dreamroute/mybatis/pro/sample/springmvc/Empty.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springmvc; 2 | 3 | public class Empty {} 4 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 描述:日期的序列化和反序列化 3 | * 4 | * @author w.dehi.2021-12-19 5 | */ 6 | package com.github.dreamroute.mybatis.pro.base.codec.date; -------------------------------------------------------------------------------- /mybatis-pro-core/src/test/java/com/github/dreamroute/mybatis/pro/core/Demo.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Table; 4 | 5 | @Table("smart_demo") 6 | public class Demo {} 7 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/c/d/DD.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.c.d; 2 | 3 | /** 4 | * 描述: 5 | * 6 | * @author w.dehai.2023/6/15.17:01 7 | */ 8 | public class DD { 9 | } 10 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/c/e/x/XX.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.c.e.x; 2 | 3 | /** 4 | * 描述: 5 | * 6 | * @author w.dehai.2023/6/15.17:01 7 | */ 8 | public class XX { 9 | } 10 | -------------------------------------------------------------------------------- /mybatis-pro-auto-configuration/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.github.dreamroute.mybatis.pro.autoconfiguration.MyBatisProAutoConfiguration, \ 3 | com.github.dreamroute.mybatis.pro.interceptor.LimitColumnInterceptor -------------------------------------------------------------------------------- /mybatis-pro-sdk/src/main/java/com/github/dreamroute/mybatis/pro/sdk/Mapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sdk; 2 | 3 | /** 4 | * crud接口集合 5 | * 6 | * @author w.dehai 7 | */ 8 | public interface Mapper extends 9 | SelectMapper, 10 | InsertMapper, 11 | UpdateMapper, 12 | DeleteMapper {} 13 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/annotations/Type.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.annotations; 2 | 3 | /** 4 | * id策略,支持自增、自定义,默认自增 5 | * 6 | * @author w.dehai 7 | */ 8 | public enum Type { 9 | /** 10 | * 自增 11 | */ 12 | IDENTITY, 13 | /** 14 | * 自定义 15 | */ 16 | AUTO 17 | } 18 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/test/java/com/githu/dreamroute/mybatis/pro/base/User.java: -------------------------------------------------------------------------------- 1 | package com.githu.dreamroute.mybatis.pro.base; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author w.dehai.2021/8/10.14:50 9 | */ 10 | @Data 11 | public class User { 12 | private Long id; 13 | private Gender gender; 14 | private Date birthday; 15 | } 16 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/ToLineThreadLocal.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | /** 4 | * @author : w.dehai.2021.04.01 5 | */ 6 | public class ToLineThreadLocal { 7 | private ToLineThreadLocal() {} 8 | 9 | public static final ThreadLocal TO_LINE = new ThreadLocal<>(); 10 | } 11 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/MyBatisProConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | 5 | /** 6 | * @author w.dehai 7 | */ 8 | @EnableConfigurationProperties(MyBatisProProperties.class) 9 | public class MyBatisProConfig {} 10 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/mapper/BaseMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.sdk.Mapper; 4 | 5 | /** 6 | * 基础mapper,包含了基础的CRUD方法,原则上业务mapper都应该实现此mapper 7 | * 8 | * @author w.dehai 9 | */ 10 | public interface BaseMapper extends Mapper {} 11 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/domain/Demo.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author w.dehi.2021-12-20 9 | */ 10 | @Data 11 | public class Demo { 12 | private Gender gender; 13 | private Date birthday; 14 | } 15 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/LogicalDeleteType.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | /** 4 | * 逻辑删除方式 5 | * 6 | * @author w.dehai.2021/9/8.15:56 7 | */ 8 | public enum LogicalDeleteType { 9 | 10 | /** 11 | * 备份方式 12 | */ 13 | BACKUP, 14 | 15 | /** 16 | * 更新状态方式 17 | */ 18 | UPDATE; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/a/mapper/Amapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.a.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 4 | 5 | /** 6 | * 描述:// TODO 此处必填 7 | * 8 | * @author w.dehi.2022-02-28 9 | */ 10 | public interface Amapper extends BaseMapper { 11 | } 12 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/b/mapper/Bmapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.b.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 4 | 5 | /** 6 | * 描述:// TODO 此处必填 7 | * 8 | * @author w.dehi.2022-02-28 9 | */ 10 | public interface Bmapper extends BaseMapper { 11 | } 12 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/config/MapperConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.config; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * 用于测试多个@MapperScan,没有别的用途 8 | */ 9 | @Configuration 10 | @MapperScan("xxx") 11 | public class MapperConfig { 12 | } 13 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/mapper/EnumTypeHandlerMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.EnumTypeHandler; 4 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 5 | 6 | public interface EnumTypeHandlerMapper extends BaseMapper {} 7 | -------------------------------------------------------------------------------- /mybatis-pro-sdk/src/main/java/com/github/dreamroute/mybatis/pro/sdk/DeleteMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sdk; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 删除接口 7 | * 8 | * @author w.dehai 9 | */ 10 | public interface DeleteMapper { 11 | 12 | /** 13 | * 根据id删除 14 | */ 15 | int deleteById(ID id); 16 | 17 | /** 18 | * 根据id批量删除 19 | */ 20 | int deleteByIds(List ids); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.service; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.service.service.BaseService; 5 | 6 | /** 7 | * @author w.dehai 8 | */ 9 | public interface UserService extends BaseService { 10 | } 11 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/service/DictService.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.service; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 4 | import com.github.dreamroute.mybatis.pro.service.service.BaseService; 5 | 6 | /** 7 | * 8 | * @author w.dehai 9 | * 10 | */ 11 | public interface DictService extends BaseService {} 12 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/test/java/com/github/dreamroute/mybatis/pro/core/DemoMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core; 2 | 3 | import com.github.dreamroute.mybatis.pro.sdk.Mapper; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author w.dehai 9 | */ 10 | public interface DemoMapper extends Mapper { 11 | 12 | Demo findByNameAndPassword(String name, String password); 13 | 14 | List findByName(String name); 15 | 16 | Demo findById(Long id); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/test/java/com/githu/dreamroute/mybatis/pro/base/MiscTest.java: -------------------------------------------------------------------------------- 1 | package com.githu.dreamroute.mybatis.pro.base; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.EnumSet; 6 | 7 | class MiscTest { 8 | 9 | @Test 10 | void enumSetTest() { 11 | EnumSet genders = EnumSet.allOf(Gender.class); 12 | System.err.println(genders); 13 | EnumSet female = EnumSet.of(Gender.FEMALE); 14 | System.err.println(female); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/id/Ids.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.id; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.ElementNotEmpty; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 前端请求中只有多个id参数 10 | * 11 | * @author w.dehai 12 | */ 13 | @Data 14 | public class Ids implements Serializable { 15 | 16 | @ElementNotEmpty 17 | private Long[] ids; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/annotations/Transient.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 被标记的字段不保存到数据 10 | * 11 | * @author w.dehai 12 | */ 13 | @Target(ElementType.FIELD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Transient { 16 | } 17 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/entity/IdEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.entity; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Id; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.SuperBuilder; 8 | 9 | @Data 10 | @SuperBuilder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class IdEntity { 14 | @Id 15 | private Long id; 16 | } 17 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/test/java/com/githu/dreamroute/mybatis/pro/base/Gender.java: -------------------------------------------------------------------------------- 1 | package com.githu.dreamroute.mybatis.pro.base; 2 | 3 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarker; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | /** 8 | * @author w.dehai.2021/8/10.14:49 9 | */ 10 | @Getter 11 | @AllArgsConstructor 12 | public enum Gender implements EnumMarker { 13 | MALE(1, "男"), 14 | FEMALE(2, "女"); 15 | 16 | private final Integer value; 17 | private final String desc; 18 | } 19 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/domain/EnumTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.domain; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Id; 4 | import com.github.dreamroute.mybatis.pro.core.annotations.Table; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Table("smart_typehandler") 9 | public class EnumTypeHandler { 10 | @Id 11 | private Long id; 12 | private Gender gender; 13 | private Integer status; 14 | } 15 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/domain/Gender.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.domain; 2 | 3 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarker; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | public enum Gender implements EnumMarker { 10 | 11 | MALE(1, "男"), FEMALE(2, "女"); 12 | 13 | private final Integer value; 14 | private final String desc; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /mybatis-pro-sdk/src/main/java/com/github/dreamroute/mybatis/pro/sdk/UpdateMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sdk; 2 | 3 | import com.github.dreamroute.locker.anno.Locker; 4 | 5 | /** 6 | * 更新接口 7 | * 8 | * @author w.dehai 9 | */ 10 | public interface UpdateMapper { 11 | 12 | /** 13 | * 根据id修改(实体为空的属性也更新到数据库) 14 | */ 15 | @Locker 16 | int updateById(T entity); 17 | 18 | /** 19 | * 根据id修改(实体为空的属性不更新到数据库) 20 | */ 21 | @Locker 22 | int updateByIdExcludeNull(T entity); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/annotations/Column.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 列名映射,默认情况下驼峰自动转下划线 10 | * 11 | * @author w.dehai 12 | */ 13 | @Target(ElementType.FIELD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Column { 16 | String value() default ""; 17 | } 18 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/interceptor/LogicalDeleteInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.interceptor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | /** 7 | * 描述:逻辑删除插件测试 8 | * 9 | * @author w.dehi.2022-01-11 10 | */ 11 | @SpringBootTest 12 | class LogicalDeleteInterceptorTest { 13 | 14 | @Test 15 | void backupTest() { 16 | // 测试事务 17 | // 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/annotations/Table.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 标注在实体上,用于设置表名 10 | * 11 | * @author w.dehai 12 | */ 13 | @Target(ElementType.TYPE) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Table { 16 | 17 | /** 18 | * 表名 19 | */ 20 | String value(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/annotations/Id.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 主键注解 10 | * 11 | * @author w.dehai 12 | */ 13 | @Target(ElementType.FIELD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Id { 16 | 17 | /** 18 | * 默认自增 19 | */ 20 | Type type() default Type.IDENTITY; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mybatis-pro-sdk/src/main/java/com/github/dreamroute/mybatis/pro/sdk/InsertMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sdk; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 新增接口 7 | * 8 | * @author w.dehai 9 | */ 10 | public interface InsertMapper { 11 | 12 | /** 13 | * 新增单个 14 | */ 15 | int insert(T entity); 16 | 17 | /** 18 | * 单个新增(实体为空的属性不新增到数据,使用数据库默认值) 19 | */ 20 | int insertExcludeNull(T entity); 21 | 22 | /** 23 | * 批量新增(实体为空的属性也更新到数据库,目前还做不到insertListExclude) 24 | */ 25 | int insertList(List entityList); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.SuperBuilder; 8 | 9 | @Data 10 | @SuperBuilder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @EqualsAndHashCode(callSuper = true) 14 | public class BaseEntity extends IdEntity { 15 | private Long createTime; 16 | private String createUser; 17 | } 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/mapper/DictMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 4 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Select; 6 | 7 | /** 8 | * 9 | * @author w.dehai 10 | * 11 | */ 12 | public interface DictMapper extends BaseMapper { 13 | 14 | @Select("select * from smart_dict where id = 1") 15 | Dict alias(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/id/Id.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.id; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Max; 6 | import javax.validation.constraints.Min; 7 | import javax.validation.constraints.NotNull; 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 前端请求中只只有1个id参数 12 | * 13 | * @author w.dehai 14 | */ 15 | @Data 16 | public class Id implements Serializable { 17 | 18 | @NotNull 19 | @Min(value = Long.MIN_VALUE) 20 | @Max(value = Long.MAX_VALUE) 21 | private Long id; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/util/anno/Dict.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.util.anno; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 标记实体中从字典取值的注解 10 | * 11 | * @author w.dehai 12 | */ 13 | 14 | @Target(ElementType.FIELD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Dict { 17 | 18 | /** 19 | * 手动指定DictMap的enName 20 | */ 21 | String value() default ""; 22 | } 23 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.service.impl; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.service.UserService; 5 | import com.github.dreamroute.mybatis.pro.service.service.AbstractServiceImpl; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * @author w.dehai 10 | */ 11 | @Service 12 | public class UserServiceImpl extends AbstractServiceImpl implements UserService { 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/service/impl/DictServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.service.impl; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.service.DictService; 5 | import com.github.dreamroute.mybatis.pro.service.service.AbstractServiceImpl; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * 10 | * @author w.dehai 11 | * 12 | */ 13 | @Service 14 | public class DictServiceImpl extends AbstractServiceImpl implements DictService {} 15 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=10000 2 | 3 | mybatis.mapper-locations=/mapper/* 4 | mybatis.configuration.map-underscore-to-camel-case=true 5 | 6 | mybatis.pro.enable-enum-type-handler = true 7 | mybatis.pro.enable-logical-delete=true 8 | #mybatis.pro.logical-delete-type=update 9 | 10 | spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver 11 | spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/smart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false&rewriteBatchedStatements=true 12 | spring.datasource.hikari.username=root 13 | spring.datasource.hikari.password=12345678 -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/MapperLabel.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * mapper.xml关键字 8 | * 9 | * @author w.dehai 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum MapperLabel { 14 | 15 | INSERT("insert"), 16 | DELETE("delete"), 17 | UPDATE("update"), 18 | SELECT("select"), 19 | 20 | MAPPER("mapper"), 21 | NAMESPACE("namespace"), 22 | ID("id"), 23 | RESULT_TYPE("resultType"), 24 | USE_GENERATED_KEYS("useGeneratedKeys"), 25 | KEY_PROPERTY("keyProperty"); 26 | 27 | private final String code; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/exception/MyBatisProException.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.exception; 2 | 3 | /** 4 | * @author w.dehai 5 | */ 6 | public class MyBatisProException extends RuntimeException { 7 | /** 8 | * 9 | */ 10 | private static final long serialVersionUID = -7785085024619152403L; 11 | 12 | public MyBatisProException() {} 13 | 14 | public MyBatisProException(String message) { 15 | super(message); 16 | } 17 | 18 | public MyBatisProException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public MyBatisProException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/Application.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot; 2 | 3 | import com.github.dreamroute.sqlprinter.starter.anno.EnableSQLPrinter; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 8 | 9 | @EnableSQLPrinter 10 | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) 11 | @MapperScan("com.github.dreamroute.mybatis.pro.sample.springboot.mapper") 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-mvc/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro-samples 7 | ${revision} 8 | 9 | spring-mvc 10 | ${project.artifactId} 11 | 12 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /mybatis-pro-sdk/src/main/java/com/github/dreamroute/mybatis/pro/sdk/SelectMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sdk; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 查询接口 7 | * 8 | * @author w.dehai 9 | */ 10 | public interface SelectMapper { 11 | 12 | /** 13 | * 根据id查询单个 14 | * 15 | * @param id 主键id 16 | * @param cols 需要查询的列名,不传代表查询全部列 17 | * @return 返回实体对象 18 | */ 19 | T selectById(ID id, String... cols); 20 | 21 | /** 22 | * 根据多个id查询多个 23 | * 24 | * @param list 主键id列表 25 | * @param cols 需要查询的列名,不传代表查询全部列 26 | * @return 返回实体对象集合 27 | */ 28 | List selectByIds(List list, String... cols); 29 | 30 | /** 31 | * 全表查询 32 | * 33 | * @param cols 需要查询的列名,不传代表查询全部列 34 | * @return 返回当前表的所有数据 35 | */ 36 | List selectAll(String... cols); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/LocalDateSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.LocalDateTimeUtil; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | import java.time.LocalDate; 10 | 11 | /** 12 | * 描述:日期序列化,将{@link java.time.LocalDate}类型转换成'yyyy-MM-dd' 13 | * 14 | * @author w.dehi.2021-12-19 15 | */ 16 | public class LocalDateSerializer extends JsonSerializer { 17 | 18 | @Override 19 | public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 20 | if (value != null) { 21 | gen.writeString(LocalDateTimeUtil.formatNormal(value)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/domain/Dict.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.domain; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Table; 4 | import com.github.dreamroute.mybatis.pro.service.entity.IdEntity; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | import lombok.experimental.SuperBuilder; 10 | 11 | /** 12 | * 13 | * @author w.dehai 14 | * 15 | */ 16 | @Data 17 | @SuperBuilder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @EqualsAndHashCode(callSuper = true) 21 | @Table("smart_dict") 22 | public class Dict extends IdEntity { 23 | private Integer value; 24 | private String labelValue; 25 | private String enName; 26 | private String cnName; 27 | private Integer sort; 28 | private Integer status; 29 | } -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/DateSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | import java.util.Date; 10 | 11 | /** 12 | * 描述:日期序列化,将{@link java.util.Date}类型转换成'yyyy-MM-dd HH:mm:ss' 13 | * 14 | * @author w.dehi.2021-12-19 15 | */ 16 | public class DateSerializer extends JsonSerializer { 17 | 18 | public static final String FORMAT = "yyyy-MM-dd HH:mm:ss"; 19 | @Override 20 | public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 21 | if (value != null) { 22 | gen.writeString(DateUtil.format(value, FORMAT)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/LocalDateTimeSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.LocalDateTimeUtil; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * 描述:日期序列化,将{@link java.time.LocalDateTime}类型转换成'yyyy-MM-dd HH:mm:ss' 13 | * 14 | * @author w.dehi.2021-12-19 15 | */ 16 | public class LocalDateTimeSerializer extends JsonSerializer { 17 | 18 | @Override 19 | public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 20 | if (value != null) { 21 | gen.writeString(LocalDateTimeUtil.formatNormal(value)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/validator/impl/ElementNotEmptyValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.validator.impl; 2 | 3 | 4 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.ElementNotEmpty; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | 9 | /** 10 | * 11 | * @author w.dehai 12 | * 13 | */ 14 | public class ElementNotEmptyValidator implements ConstraintValidator { 15 | 16 | @Override 17 | public boolean isValid(Object[] array, ConstraintValidatorContext context) { 18 | // 不判断数据为空的情况,此情况分发给其他的校验器。 19 | if (null == array || 0 == array.length) { 20 | return true; 21 | } 22 | for (int i = 0; i < array.length; i++) { 23 | if (null == array[i]) { 24 | return false; 25 | } 26 | } 27 | return true; 28 | } 29 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | /.flattened-pom.xml 35 | /mybatis-pro-service/.flattened-pom.xml 36 | /mybatis-pro-sdk/.flattened-pom.xml 37 | /mybatis-pro-samples/.flattened-pom.xml 38 | /mybatis-pro-samples/spring-mvc/.flattened-pom.xml 39 | /mybatis-pro-samples/spring-boot/.flattened-pom.xml 40 | /mybatis-pro-core/.flattened-pom.xml 41 | /mybatis-pro-boot-starter/.flattened-pom.xml 42 | /mybatis-pro-auto-configuration/.flattened-pom.xml 43 | /mybatis-pro-base/.flattened-pom.xml 44 | /mybatis-pro-interceptor/.flattened-pom.xml 45 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.domain; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Column; 4 | import com.github.dreamroute.mybatis.pro.core.annotations.Id; 5 | import com.github.dreamroute.mybatis.pro.core.annotations.Table; 6 | import com.github.dreamroute.mybatis.pro.core.annotations.Transient; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import lombok.experimental.SuperBuilder; 11 | 12 | @Data 13 | @SuperBuilder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Table("smart_user") 17 | public class User { 18 | 19 | @Id 20 | private Long id; 21 | private String name; 22 | private String password; 23 | private Long version; 24 | @Transient 25 | private Integer gender; 26 | private String phoneNo; 27 | 28 | @Column("addr_info") 29 | private String addr; 30 | private String orderId; 31 | 32 | private Integer status; 33 | } 34 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * 描述:EnumMarker Jackson序列化,将EnumMarkder序列化成getValue()方式: 11 | *
12 |  *     {
13 |  *         "name": "w.dehi",
14 |  *         "gender": EnumMarkder.getValue()
15 |  *     }
16 |  * 
17 | * 18 | * @author w.dehi.2021-12-19 19 | */ 20 | @SuppressWarnings("rawtypes") 21 | public class EnumMarkerSerializer extends JsonSerializer { 22 | @Override 23 | public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 24 | if (value instanceof EnumMarker) { 25 | EnumMarker v = (EnumMarker) value; 26 | gen.writeObject(v.getValue()); 27 | } else { 28 | gen.writeObject(value.toString()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/test/java/com/github/dreamroute/mybatis/pro/core/EnumTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core; 2 | 3 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarker; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static com.github.dreamroute.mybatis.pro.core.EnumTest.Gender.FEMALE; 9 | import static com.github.dreamroute.mybatis.pro.core.EnumTest.Gender.MALE; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertThrows; 12 | 13 | class EnumTest { 14 | 15 | @Getter 16 | @AllArgsConstructor 17 | enum Gender implements EnumMarker { 18 | 19 | MALE(1, "男"), 20 | FEMALE(2, "女"); 21 | 22 | private final Integer value; 23 | private final String desc; 24 | 25 | } 26 | 27 | @Test 28 | void createEnumTest() { 29 | Gender male = EnumMarker.valueOf(Gender.class, 1); 30 | assertEquals(MALE, male); 31 | Gender female = EnumMarker.valueOf(Gender.class, 2); 32 | assertEquals(FEMALE, female); 33 | 34 | // 抛异常 35 | assertThrows(IllegalArgumentException.class, () -> EnumMarker.valueOf(Gender.class, 3)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarker.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import java.io.Serializable; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.Stream; 6 | 7 | /** 8 | * 枚举类型标记接口,实现此接口的枚举类型会被mybatis自动进行转型 9 | * 10 | * @author w.dehai 11 | */ 12 | public interface EnumMarker extends Serializable { 13 | 14 | /** 15 | * 返回枚举的value字段的值 16 | * 17 | * @return 返回枚举的value字段的值 18 | */ 19 | Integer getValue(); 20 | 21 | /** 22 | * 返回枚举描述信息 23 | * 24 | * @return 返回描述信息 25 | */ 26 | String getDesc(); 27 | 28 | /** 29 | * 根据value字段的值返回对应的枚举类型 30 | * 31 | * @param enumCls 枚举类型 32 | * @param value Integer类型的值 33 | * @return 返回具体的枚举类型 34 | */ 35 | static E valueOf(Class enumCls, int value) { 36 | E[] enumConstants = enumCls.getEnumConstants(); 37 | for (E e : enumConstants) { 38 | if (e.getValue() == value) { 39 | return e; 40 | } 41 | } 42 | String valueList = Stream.of(enumConstants).map(EnumMarker::getValue).map(String::valueOf).collect(Collectors.joining(", ", "[", "]")); 43 | throw new IllegalArgumentException("枚举值[" + value + "]不在" + enumCls.getSimpleName() + "的取值范围" + valueList + "之内"); 44 | } 45 | } -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import com.github.dreamroute.mybatis.pro.base.codec.PropertyAliasCache; 7 | import org.springframework.beans.BeanUtils; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 描述:EnumMarker Jackson反序列化 13 | * 14 | * @author w.dehi.2021-12-19 15 | */ 16 | public class EnumMarkerDeserializer extends JsonDeserializer> { 17 | @Override 18 | public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 19 | String name = PropertyAliasCache.getFieldAliasMap(p); 20 | Class propertyType = BeanUtils.findPropertyType(name, p.getCurrentValue().getClass()); 21 | 22 | int intValue = p.getIntValue(); 23 | @SuppressWarnings("unchecked") 24 | Class c = (Class) propertyType; 25 | EnumMarker enumMarker = EnumMarker.valueOf(c, intValue); 26 | 27 | @SuppressWarnings("unchecked") 28 | Enum resp = (Enum) enumMarker; 29 | return resp; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/util/DictFieldCache.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.util; 2 | 3 | import cn.hutool.core.util.ReflectUtil; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.util.anno.Dict; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.List; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | import java.util.concurrent.ConcurrentMap; 10 | 11 | import static java.util.Arrays.stream; 12 | import static java.util.Optional.ofNullable; 13 | import static java.util.stream.Collectors.toList; 14 | 15 | /** 16 | * 字典属性缓存:被@Dict标记的属性缓存 17 | * 18 | * @author w.dehai 19 | * 20 | */ 21 | public class DictFieldCache { 22 | private DictFieldCache() {} 23 | 24 | /** 缓存带有@Dict的对象属性 **/ 25 | private static final ConcurrentMap, List> DICT_MAP = new ConcurrentHashMap<>(); 26 | 27 | public static List findForClass(Class cls) { 28 | return DICT_MAP.computeIfAbsent(cls, c -> { 29 | Field[] fields = ReflectUtil.getFields(c); 30 | return stream(ofNullable(fields).orElseGet(() -> new Field[0])) 31 | .filter(field -> field.isAnnotationPresent(Dict.class)) 32 | .peek(field -> field.setAccessible(true)) 33 | .collect(toList()); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/misc/ClassPathUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.misc; 2 | 3 | import com.google.common.collect.Sets; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.util.ObjectUtils; 6 | 7 | import java.util.HashSet; 8 | 9 | import static com.github.dreamroute.mybatis.pro.base.util.ClassPathUtil.resolvePackage; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | /** 13 | * 描述:通配符路径解析测试 14 | * 15 | * @author w.dehi.2022-02-28 16 | */ 17 | class ClassPathUtilTest { 18 | @Test 19 | void resolvePackageTest() { 20 | String path = "com.github.dreamroute.mybatis.pro.sample.springboot.*.mapper"; 21 | String a = "com.github.dreamroute.mybatis.pro.sample.springboot.a.mapper"; 22 | String b = "com.github.dreamroute.mybatis.pro.sample.springboot.b.mapper"; 23 | String[] pkg = resolvePackage(path); 24 | HashSet result = Sets.newHashSet(pkg); 25 | assertTrue(result.contains(a)); 26 | assertTrue(result.contains(b)); 27 | 28 | String basePath = "com.github.dreamroute.mybatis.pro.sample.springboot.c"; 29 | String[] pkgs = resolvePackage(basePath); 30 | if (!ObjectUtils.isEmpty(pkgs)) { 31 | for (String p : pkgs) { 32 | System.err.println(p); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerSerializerForWeb.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import lombok.Getter; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 描述:EnumMarker Jackson序列化,将EnumMarkder序列化成对象方式,用途是返回给前端页面使用: 13 | *
14 |  *     {
15 |  *         "name": "w.dehi",
16 |  *         "gender": {
17 |  *             "value": EnumMarker.getValue(),
18 |  *             "desc": EnumMarker.getDesc()
19 |  *         }
20 |  *     }
21 |  * 
22 | * 23 | * @author w.dehi.2021-12-19 24 | */ 25 | public class EnumMarkerSerializerForWeb extends JsonSerializer { 26 | public EnumMarkerSerializerForWeb() { 27 | } 28 | 29 | public void serialize(EnumMarker value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 30 | gen.writeObject(new EnumObj(value.getValue(), value.getDesc())); 31 | // -- 也可以使用下面方式输出给前端,效果一样 32 | // gen.writeStartObject(); 33 | // gen.writeNumberField("value", v.getValue()); 34 | // gen.writeStringField("desc", v.getDesc()); 35 | // gen.writeEndObject(); 36 | } 37 | 38 | @Getter 39 | @RequiredArgsConstructor 40 | private static class EnumObj { 41 | private final Integer value; 42 | private final String desc; 43 | } 44 | } -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `smart_user`; 2 | DROP TABLE IF EXISTS `smart_typehandler`; 3 | DROP TABLE IF EXISTS `smart_dict`; 4 | DROP TABLE IF EXISTS `logical_delete`; 5 | 6 | CREATE TABLE `smart_user` ( 7 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 8 | `name` varchar(32) DEFAULT NULL, 9 | `password` varchar(32) DEFAULT '123456', 10 | `version` bigint(20) DEFAULT NULL, 11 | `phone_no` varchar(20) DEFAULT NULL, 12 | `addr_info` varchar(255) DEFAULT NULL, 13 | `order_id` varchar(50) DEFAULT NULL, 14 | `status` tinyint(4) DEFAULT NULL, 15 | PRIMARY KEY (`id`) 16 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 17 | 18 | CREATE TABLE `smart_typehandler` ( 19 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 20 | `gender` tinyint(4) DEFAULT NULL, 21 | PRIMARY KEY (`id`) 22 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 23 | 24 | CREATE TABLE `smart_dict` ( 25 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 26 | `value` tinyint(4) DEFAULT NULL, 27 | `label_value` varchar(50) DEFAULT NULL, 28 | `en_name` varchar(50) DEFAULT NULL, 29 | `cn_name` varchar(50) DEFAULT NULL, 30 | `sort` int(11) DEFAULT NULL, 31 | `status` tinyint null, 32 | PRIMARY KEY (`id`) 33 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 34 | 35 | CREATE TABLE `logical_delete` ( 36 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 37 | `table_name` varchar(100) NOT NULL, 38 | `data` longtext NOT NULL, 39 | `delete_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 40 | PRIMARY KEY (`id`) 41 | ); 42 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerSerializerForExtra.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import cn.hutool.core.text.CharSequenceUtil; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * 描述:EnumMarker Jackson序列化,将EnumMarkder序列化成getValue(),并且为对象增加一个以Desc结尾的枚举字段方式: 12 | *
13 |  *     {
14 |  *         "name": "w.dehai",
15 |  *         "gender": 1,
16 |  *     }
17 |  *     那么输出(嵌套对象也会添加一个字段):
18 |  *     {
19 |  *         "name": "w.dehi",
20 |  *         "gender": 1,
21 |  *         "genderDesc": "男"
22 |  *     }
23 |  * 
24 | * 25 | * @author w.dehi.2021-12-19 26 | */ 27 | @SuppressWarnings("rawtypes") 28 | public class EnumMarkerSerializerForExtra extends JsonSerializer { 29 | @Override 30 | public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 31 | if (value instanceof EnumMarker) { 32 | EnumMarker v = (EnumMarker) value; 33 | gen.writeObject(v.getValue()); 34 | String currentName = gen.getOutputContext().getCurrentName(); 35 | if (CharSequenceUtil.isBlank(currentName)) { 36 | currentName = gen.getOutputContext().getParent().getCurrentName(); 37 | } 38 | gen.writeObjectField(currentName + "Desc", v.getDesc()); 39 | } else { 40 | gen.writeObject(value.toString()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/validator/ElementNotEmpty.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.validator; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.impl.ElementNotEmptyValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Repeatable; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.Target; 11 | 12 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 13 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 14 | import static java.lang.annotation.ElementType.FIELD; 15 | import static java.lang.annotation.ElementType.METHOD; 16 | import static java.lang.annotation.ElementType.PARAMETER; 17 | import static java.lang.annotation.ElementType.TYPE_USE; 18 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 19 | 20 | /** 21 | * 22 | * @author w.dehai 23 | * 24 | */ 25 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 26 | @Retention(RUNTIME) 27 | @Documented 28 | @Repeatable(ElementNotEmpty.List.class) 29 | @Constraint(validatedBy = {ElementNotEmptyValidator.class}) 30 | public @interface ElementNotEmpty { 31 | 32 | String message() default "子元素不能为空"; 33 | 34 | Class[] groups() default {}; 35 | 36 | Class[] payload() default {}; 37 | 38 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 39 | @Retention(RUNTIME) 40 | @Documented 41 | @interface List { 42 | ElementNotEmpty[] value(); 43 | } 44 | } -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/validator/impl/TimeValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.validator.impl; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.Time; 4 | import org.apache.commons.lang3.math.NumberUtils; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | 11 | /** 12 | * mysql timestamp范围:1971-01-01 08:00:01 到2038-01-19 11:14:07,本来是从00:00:00开始的,但是中国属于UTC时区,需要增加8小时,这里我们前后缩减一年,保证正确性 13 | * 14 | * @author w.dehai 15 | * 16 | */ 17 | public class TimeValidator implements ConstraintValidator { 18 | 19 | @Override 20 | public boolean isValid(String value, ConstraintValidatorContext context) { 21 | 22 | // 如果为空,不做处理 23 | if (value == null || value.length() == 0) { 24 | return true; 25 | } 26 | 27 | // 如果不是数字,报错 28 | if (!NumberUtils.isCreatable(value)) { 29 | return false; 30 | } 31 | 32 | // 处理数字情况 33 | try { 34 | long from = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1972-01-01 00:00:00").getTime(); 35 | long to = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2037-12-31 00:00:00").getTime(); 36 | long time = Long.parseLong(value); 37 | if (time < from || time > to) { 38 | return false; 39 | } 40 | } catch (ParseException e) { 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/DateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | import com.fasterxml.jackson.core.JsonParser; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.github.dreamroute.mybatis.pro.base.codec.PropertyAliasCache; 9 | import org.springframework.beans.BeanUtils; 10 | 11 | import java.io.IOException; 12 | import java.util.Date; 13 | 14 | import static com.github.dreamroute.mybatis.pro.base.codec.date.DateSerializer.FORMAT; 15 | 16 | /** 17 | * 描述:日期反序列化,将'yyyy-MM-dd HH:mm:ss.SSS'反序列化成{@link java.util.Date}类型 18 | * 19 | * @author w.dehi.2021-12-19 20 | */ 21 | public class DateDeserializer extends JsonDeserializer { 22 | @Override 23 | public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 24 | String name = PropertyAliasCache.getFieldAliasMap(p); 25 | Class propertyType = BeanUtils.findPropertyType(name, p.getCurrentValue().getClass()); 26 | if (Date.class.isAssignableFrom(propertyType)) { 27 | String dateStr = p.getValueAsString(); 28 | if (CharSequenceUtil.isNotBlank(dateStr)) { 29 | try { 30 | return DateUtil.parse(dateStr, FORMAT); 31 | } catch (Exception e) { 32 | throw new IllegalArgumentException("日期格式错误, 当前日期为: " + dateStr + ", 需要yyyy-MM-dd HH:mm:ss格式"); 33 | } 34 | } 35 | } 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mybatis-pro-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro 7 | ${revision} 8 | 9 | mybatis-pro-samples 10 | ${project.artifactId} 11 | pom 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-web 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-devtools 21 | true 22 | 23 | 24 | com.ninja-squad 25 | DbSetup 26 | 2.1.0 27 | 28 | 29 | javax.validation 30 | validation-api 31 | 2.0.1.Final 32 | 33 | 34 | mysql 35 | mysql-connector-java 36 | 37 | 38 | 39 | 40 | spring-boot 41 | spring-mvc 42 | 43 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/LocalDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.DatePattern; 4 | import cn.hutool.core.date.LocalDateTimeUtil; 5 | import cn.hutool.core.text.CharSequenceUtil; 6 | import com.fasterxml.jackson.core.JsonParser; 7 | import com.fasterxml.jackson.databind.DeserializationContext; 8 | import com.fasterxml.jackson.databind.JsonDeserializer; 9 | import com.github.dreamroute.mybatis.pro.base.codec.PropertyAliasCache; 10 | import org.springframework.beans.BeanUtils; 11 | 12 | import java.io.IOException; 13 | import java.time.LocalDate; 14 | 15 | /** 16 | * 描述:日期反序列化,将'yyyy-MM-dd HH:mm:ss'反序列化成{@link java.time.LocalDate}类型 17 | * 18 | * @author w.dehi.2021-12-19 19 | */ 20 | public class LocalDateDeserializer extends JsonDeserializer { 21 | @Override 22 | public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 23 | String name = PropertyAliasCache.getFieldAliasMap(p); 24 | Class propertyType = BeanUtils.findPropertyType(name, p.getCurrentValue().getClass()); 25 | if (LocalDate.class.isAssignableFrom(propertyType)) { 26 | String dateStr = p.getValueAsString(); 27 | if (CharSequenceUtil.isNotBlank(dateStr)) { 28 | try { 29 | return LocalDateTimeUtil.parseDate(dateStr, DatePattern.NORM_DATE_FORMATTER); 30 | } catch (Exception e) { 31 | throw new IllegalArgumentException("日期格式错误, 当前日期为: " + dateStr + ", 需要yyyy-MM-dd格式"); 32 | } 33 | } 34 | } 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/adaptor/validator/Time.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.adaptor.validator; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.Time.List; 4 | import com.github.dreamroute.mybatis.pro.service.adaptor.validator.impl.TimeValidator; 5 | 6 | import javax.validation.Constraint; 7 | import javax.validation.Payload; 8 | import java.lang.annotation.Documented; 9 | import java.lang.annotation.Repeatable; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.Target; 12 | 13 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 14 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 15 | import static java.lang.annotation.ElementType.FIELD; 16 | import static java.lang.annotation.ElementType.METHOD; 17 | import static java.lang.annotation.ElementType.PARAMETER; 18 | import static java.lang.annotation.ElementType.TYPE_USE; 19 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 20 | 21 | /** 22 | * 23 | * @author w.dehai 24 | * 25 | */ 26 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 27 | @Retention(RUNTIME) 28 | @Documented 29 | @Repeatable(List.class) 30 | @Constraint(validatedBy = {TimeValidator.class}) 31 | public @interface Time { 32 | 33 | String message() default "时间不正确, 范围1972-01-01 00:00:00 ~ 2037-12-31 00:00:00"; 34 | 35 | Class[] groups() default {}; 36 | 37 | Class[] payload() default {}; 38 | 39 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 40 | @Retention(RUNTIME) 41 | @Documented 42 | @interface List { 43 | 44 | Time[] value(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/date/LocalDateTimeDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.date; 2 | 3 | import cn.hutool.core.date.DatePattern; 4 | import cn.hutool.core.date.LocalDateTimeUtil; 5 | import cn.hutool.core.text.CharSequenceUtil; 6 | import com.fasterxml.jackson.core.JsonParser; 7 | import com.fasterxml.jackson.databind.DeserializationContext; 8 | import com.fasterxml.jackson.databind.JsonDeserializer; 9 | import com.github.dreamroute.mybatis.pro.base.codec.PropertyAliasCache; 10 | import org.springframework.beans.BeanUtils; 11 | 12 | import java.io.IOException; 13 | import java.time.LocalDateTime; 14 | 15 | /** 16 | * 描述:日期反序列化,将'yyyy-MM-dd HH:mm:ss'反序列化成{@link java.time.LocalDateTime}类型 17 | * 18 | * @author w.dehi.2021-12-19 19 | */ 20 | public class LocalDateTimeDeserializer extends JsonDeserializer { 21 | @Override 22 | public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 23 | String name = PropertyAliasCache.getFieldAliasMap(p); 24 | Class propertyType = BeanUtils.findPropertyType(name, p.getCurrentValue().getClass()); 25 | if (LocalDateTime.class.isAssignableFrom(propertyType)) { 26 | String dateStr = p.getValueAsString(); 27 | if (CharSequenceUtil.isNotBlank(dateStr)) { 28 | try { 29 | return LocalDateTimeUtil.parse(dateStr, DatePattern.NORM_DATETIME_FORMATTER); 30 | } catch (Exception e) { 31 | throw new IllegalArgumentException("日期格式错误, 当前日期为: " + dateStr + ", 需要yyyy-MM-dd HH:mm:ss格式"); 32 | } 33 | } 34 | } 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/test/java/com/github/dreamroute/mybatis/pro/core/MyBatisProUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Column; 4 | import com.github.dreamroute.mybatis.pro.core.annotations.Transient; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.io.Serializable; 10 | import java.util.Map; 11 | 12 | import static com.github.dreamroute.mybatis.pro.core.util.MyBatisProUtil.getFieldAliasMap; 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertFalse; 15 | import static org.junit.jupiter.api.Assertions.assertTrue; 16 | 17 | /** 18 | * @author w.dehai 19 | */ 20 | public class MyBatisProUtilTest { 21 | 22 | @Test 23 | public void getFieldAliasMapTest() { 24 | Map map = getFieldAliasMap(Demo.class); 25 | assertTrue(map.containsKey("id")); 26 | assertTrue(map.containsKey("name")); 27 | assertTrue(map.containsKey("phoneNo")); 28 | assertEquals("phone_no", map.get("phoneNo")); 29 | assertFalse(map.containsKey("serialVersionUID")); 30 | assertFalse(map.containsKey("tx")); 31 | 32 | } 33 | 34 | @Data 35 | public static class BaseDomain { 36 | private Long id; 37 | } 38 | 39 | @Data 40 | @EqualsAndHashCode(callSuper = true) 41 | public static class Demo extends BaseDomain implements Serializable { 42 | private static final long serialVersionUID = -1L; 43 | private String name; 44 | @Column("phone_no") 45 | private String phoneNo; 46 | @Transient 47 | private String tx; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/PropertyAliasCache.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec; 2 | 3 | import cn.hutool.core.annotation.AnnotationUtil; 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | import cn.hutool.core.util.ReflectUtil; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.core.JsonParser; 8 | 9 | import java.io.IOException; 10 | import java.lang.reflect.Field; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * 描述:企微对象别名缓存 17 | * 18 | * @author w.dehai.2024/8/26.11:16 19 | */ 20 | public class PropertyAliasCache { 21 | private PropertyAliasCache() {} 22 | 23 | private static final ConcurrentHashMap, Map> CACHE = new ConcurrentHashMap<>(); 24 | 25 | public static String getFieldAliasMap(JsonParser jsonParser) { 26 | Map map = CACHE.computeIfAbsent(jsonParser.getCurrentValue().getClass(), c -> { 27 | Map fieldMap = new HashMap<>(); 28 | Field[] fields = ReflectUtil.getFields(c); 29 | for (Field field : fields) { 30 | String fieldName = field.getName(); 31 | String annotationValue = AnnotationUtil.getAnnotationValue(field, JsonProperty.class); 32 | annotationValue = CharSequenceUtil.isBlank(annotationValue) ? fieldName : annotationValue; 33 | fieldMap.put(annotationValue, fieldName); 34 | } 35 | return fieldMap; 36 | }); 37 | try { 38 | return map.get(jsonParser.getCurrentName()); 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/condition/AliasTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.condition; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.DictMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import com.ninja_squad.dbsetup.operation.Insert; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import javax.sql.DataSource; 14 | 15 | import static com.ninja_squad.dbsetup.Operations.insertInto; 16 | import static com.ninja_squad.dbsetup.Operations.truncate; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | 19 | /** 20 | * @author w.dehai 21 | */ 22 | @SpringBootTest 23 | class AliasTest { 24 | 25 | @Autowired 26 | private DictMapper dictMapper; 27 | @Autowired 28 | private DataSource dataSource; 29 | 30 | @BeforeEach 31 | void init() { 32 | // init smat_dict 33 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_dict")).launch(); 34 | Insert insert2 = insertInto("smart_dict") 35 | .columns("value", "cn_name") 36 | .values(1, "有效") 37 | .values(0, "无效") 38 | .build(); 39 | new DbSetup(new DataSourceDestination(dataSource), insert2).launch(); 40 | } 41 | 42 | @Test 43 | void aliasTest() { 44 | Dict result = dictMapper.alias(); 45 | assertEquals("有效", result.getCnName()); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/condition/ExistByTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.condition; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import com.ninja_squad.dbsetup.operation.Insert; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | 14 | import static com.ninja_squad.dbsetup.Operations.insertInto; 15 | import static com.ninja_squad.dbsetup.Operations.truncate; 16 | import static org.junit.jupiter.api.Assertions.assertTrue; 17 | 18 | /** 19 | * @author w.dehai 20 | */ 21 | @SpringBootTest 22 | class ExistByTest { 23 | 24 | @Autowired 25 | private UserMapper userMapper; 26 | @Autowired 27 | private DataSource dataSource; 28 | 29 | @BeforeEach 30 | void init() { 31 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 32 | Insert insert = insertInto("smart_user") 33 | .columns("name", "password", "phone_no", "status") 34 | .values("w.dehai", "123456", "1306006", 1) 35 | .values("Jaedong", "123", "1306006", 1) 36 | .build(); 37 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 38 | } 39 | 40 | @Test 41 | void existByNameOrPasswordTest() { 42 | boolean exist = userMapper.existByNameOrPassword("w.dehai", "1306006"); 43 | assertTrue(exist); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/sdk/DeleteTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.sdk; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import com.ninja_squad.dbsetup.operation.Insert; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | import java.util.Arrays; 14 | 15 | import static com.ninja_squad.dbsetup.Operations.insertInto; 16 | import static com.ninja_squad.dbsetup.Operations.truncate; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | 19 | /** 20 | * @author w.dehai 21 | */ 22 | @SpringBootTest 23 | class DeleteTest { 24 | 25 | @Autowired 26 | private UserMapper userMapper; 27 | @Autowired 28 | private DataSource dataSource; 29 | 30 | @BeforeEach 31 | void init() { 32 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 33 | Insert insert = insertInto("smart_user") 34 | .columns("name") 35 | .values("w.dehai") 36 | .values("Jaedong") 37 | .build(); 38 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 39 | } 40 | 41 | @Test 42 | void deleteByIdTest() { 43 | int result = userMapper.deleteById(1L); 44 | assertEquals(1, result); 45 | } 46 | 47 | @Test 48 | void deleteByIdsTest() { 49 | int result = userMapper.deleteByIds(Arrays.asList(1L, 2L)); 50 | assertEquals(2, result); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/condition/DeleteByTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.condition; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import com.ninja_squad.dbsetup.operation.Insert; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | 14 | import static com.ninja_squad.dbsetup.Operations.insertInto; 15 | import static com.ninja_squad.dbsetup.Operations.truncate; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | 18 | /** 19 | * @author w.dehai 20 | */ 21 | @SpringBootTest 22 | class DeleteByTest { 23 | 24 | @Autowired 25 | private UserMapper userMapper; 26 | @Autowired 27 | private DataSource dataSource; 28 | 29 | @BeforeEach 30 | void init() { 31 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 32 | Insert insert = insertInto("smart_user") 33 | .columns("name", "password") 34 | .values("w.dehai", "123456") 35 | .values("Jaedong", "123") 36 | .build(); 37 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 38 | } 39 | 40 | @Test 41 | void deleteByNameOrPasswordTest() { 42 | int result = userMapper.deleteByNameOrPassword("w.dehai", "123"); 43 | assertEquals(2, result); 44 | } 45 | 46 | @Test 47 | void deleteByNameOrPasswordOptTest() { 48 | int result = userMapper.deleteByNameOrPasswordOpt("w.dehai", null); 49 | assertEquals(1, result); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/mapper/UserMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import com.ninja_squad.dbsetup.operation.Insert; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import javax.annotation.Resource; 12 | import javax.sql.DataSource; 13 | 14 | import static com.ninja_squad.dbsetup.Operations.insertInto; 15 | import static com.ninja_squad.dbsetup.Operations.truncate; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | 18 | /** 19 | * 描述: 20 | * 21 | * @author w.dehai.2023/6/8.11:31 22 | */ 23 | @SpringBootTest 24 | class UserMapperTest { 25 | 26 | @Resource 27 | private DataSource dataSource; 28 | @Resource 29 | private UserMapper userMapper; 30 | 31 | @BeforeEach 32 | void init() { 33 | 34 | // init smat_user 35 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 36 | Insert insert = insertInto("smart_user") 37 | .columns("name", "password", "phone_no", "version", "addr_info") 38 | .values("w.dehai", "123456", "1306006", 1L, "成都") 39 | .build(); 40 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 41 | } 42 | 43 | @Test 44 | void testTrim() { 45 | User user = userMapper.testTrim(null, "123456"); 46 | assertEquals("w.dehai", user.getName()); 47 | } 48 | 49 | @Test 50 | void testWhere() { 51 | User user = userMapper.testWhere(null, "123456"); 52 | assertEquals("w.dehai", user.getName()); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 13 | 14 | 20 | 21 | 24 | 25 | 33 | 34 | 41 | 42 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/typehandler/EnumTypeHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.typehandler; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.EnumTypeHandler; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.EnumTypeHandlerMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | import java.util.List; 14 | 15 | import static com.github.dreamroute.mybatis.pro.sample.springboot.domain.Gender.MALE; 16 | import static com.ninja_squad.dbsetup.Operations.truncate; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | 19 | @SpringBootTest 20 | class EnumTypeHandlerTest { 21 | 22 | @Autowired 23 | private EnumTypeHandlerMapper enumTypeHandlerMapper; 24 | @Autowired 25 | private DataSource dataSource; 26 | 27 | @BeforeEach 28 | void init() { 29 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_typehandler")).launch(); 30 | } 31 | 32 | @Test 33 | void insertTest() { 34 | EnumTypeHandler enumTypeHandler = new EnumTypeHandler(); 35 | enumTypeHandler.setGender(MALE); 36 | enumTypeHandler.setStatus(1); 37 | int result = enumTypeHandlerMapper.insert(enumTypeHandler); 38 | assertEquals(1, result); 39 | } 40 | 41 | @Test 42 | void selectTest() { 43 | EnumTypeHandler enumTypeHandler = new EnumTypeHandler(); 44 | enumTypeHandler.setGender(MALE); 45 | enumTypeHandler.setStatus(1); 46 | enumTypeHandlerMapper.insert(enumTypeHandler); 47 | List all = enumTypeHandlerMapper.selectAll(); 48 | assertEquals(1, all.size()); 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/misc/UserMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.misc; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import com.ninja_squad.dbsetup.operation.Insert; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import javax.sql.DataSource; 14 | 15 | import java.util.List; 16 | 17 | import static com.google.common.collect.Lists.newArrayList; 18 | import static com.ninja_squad.dbsetup.Operations.insertInto; 19 | import static com.ninja_squad.dbsetup.Operations.truncate; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | /** 23 | * 描述:杂项测试 24 | * 25 | * @author w.dehi.2022-02-09 26 | */ 27 | @SpringBootTest 28 | class UserMapperTest { 29 | @Autowired 30 | private UserMapper userMapper; 31 | @Autowired 32 | private DataSource dataSource; 33 | 34 | @BeforeEach 35 | void init() { 36 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 37 | Insert insert = insertInto("smart_user") 38 | .columns("name") 39 | .values("w.dehai") 40 | .values("Jaedong") 41 | .build(); 42 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 43 | } 44 | 45 | @Test 46 | void dynamicSqlSourceTest() { 47 | List users = userMapper.dynamicSqlSourceTest(newArrayList(1L, 2L)); 48 | assertEquals(2, users.size()); 49 | } 50 | 51 | @Test 52 | void rawSqlSourceTest() { 53 | User user = userMapper.rawSqlSourceTest(1L); 54 | assertEquals("w.dehai", user.getName()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/service/BaseService.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.service; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author w.dehai 7 | */ 8 | public interface BaseService { 9 | 10 | /** 11 | * 单个新增(null值也会被保存) 12 | * 13 | * @param entity 需要保存的实体对象 14 | * @return 返回保存成功的实体(自增主键会包含在返回值中) 15 | */ 16 | T insert(T entity); 17 | 18 | /** 19 | * 单个新增(null值会被忽略) 20 | * 21 | * @param entity 需要保存的实体对象 22 | * @return 返回保存成功的实体(自增主键会包含在返回值中) 23 | */ 24 | T insertExcludeNull(T entity); 25 | 26 | /** 27 | * 批量新增(null值会被忽略) 28 | * 29 | * @param entityList 需要保存的实体对象列表 30 | * @return 返回保存成功的实体(自增主键会包含在返回值中) 31 | */ 32 | List insertList(List entityList); 33 | 34 | 35 | /** 36 | * 批量新增(null值会被忽略) 37 | * 38 | * @param entityList 需要保存的实体对象列表 39 | * @param partition entityList会按照没批partition条数,分批次保存,避免数据太大报错 40 | * @return 返回保存成功的实体(自增主键会包含在返回值中) 41 | */ 42 | List insertList(List entityList, int partition); 43 | 44 | /** 45 | * 根据id删除单个 46 | */ 47 | int delete(ID id); 48 | 49 | /** 50 | * 根据id批量删除 51 | */ 52 | int delete(List ids); 53 | 54 | /** 55 | * 根据id更新(实体属性为空的列也将更新到数据) 56 | */ 57 | int update(T entity); 58 | 59 | /** 60 | * 根据id更新(实体属性为空的列不更新到数据) 61 | */ 62 | int updateExcludeNull(T entity); 63 | 64 | /** 65 | * 根据id单个查询 66 | * 67 | * @param id 主键 68 | * @param cols 需要返回的列名数组 69 | * @return 返回id对应的数据 70 | */ 71 | T selectById(ID id, String... cols); 72 | 73 | /** 74 | * 根据id批量查询 75 | * 76 | * @param ids 主键列表 77 | * @param cols 需要返回的列名数组 78 | * @return 返回id对应的数据 79 | */ 80 | List selectByIds(List ids, String... cols); 81 | 82 | /** 83 | * 全表查询 84 | * 85 | * @param cols 需要返回的列名数组 86 | * @return 返回全部数据 87 | */ 88 | List selectAll(String... cols); 89 | 90 | } 91 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerSerializerForExtraCollection.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | import com.fasterxml.jackson.core.JsonGenerator; 6 | import com.fasterxml.jackson.databind.JsonSerializer; 7 | import com.fasterxml.jackson.databind.SerializerProvider; 8 | 9 | import java.io.IOException; 10 | import java.util.Collection; 11 | 12 | /** 13 | * 描述:EnumMarker Jackson序列化,将EnumMarkder序列化成getValue(),并且为对象增加一个以Desc结尾的枚举字段方式: 14 | *
15 |  *     {
16 |  *         "name": "w.dehai",
17 |  *         "gender": 1,
18 |  *     }
19 |  *     那么输出(嵌套对象也会添加一个字段):
20 |  *     {
21 |  *         "name": "w.dehi",
22 |  *         "gender": 1,
23 |  *         "genderDesc": "男"
24 |  *     }
25 |  * 
26 | * 27 | * @author w.dehi.2021-12-19 28 | */ 29 | @SuppressWarnings("rawtypes") 30 | public class EnumMarkerSerializerForExtraCollection extends JsonSerializer { 31 | @Override 32 | public void serialize(Collection value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 33 | if (value instanceof EnumMarker) { 34 | EnumMarker v = (EnumMarker) value; 35 | gen.writeObject(v.getValue()); 36 | String currentName = gen.getOutputContext().getCurrentName(); 37 | if (CharSequenceUtil.isBlank(currentName)) { 38 | currentName = gen.getOutputContext().getParent().getCurrentName(); 39 | } 40 | gen.writeObjectField(currentName + "Desc", v.getDesc()); 41 | } else if (value instanceof Collection) { 42 | if (CollUtil.isNotEmpty(value)) { 43 | value.stream().findAny().filter(e -> e instanceof EnumMarker).ifPresent(e -> { 44 | throw new IllegalArgumentException("返回值不允许是枚举类型EnumMarker的集合类型, 因为无法推断Desc字段"); 45 | }); 46 | } 47 | gen.writeObject(value); 48 | } else { 49 | gen.writeObject(value); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/util/XmlUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.util; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.exception.MyBatisProException; 4 | import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; 5 | import org.apache.ibatis.parsing.XNode; 6 | import org.apache.ibatis.parsing.XPathParser; 7 | import org.springframework.core.io.ByteArrayResource; 8 | import org.springframework.core.io.Resource; 9 | 10 | import static cn.hutool.core.util.ClassUtil.loadClass; 11 | import static com.github.dreamroute.mybatis.pro.core.consts.MapperLabel.MAPPER; 12 | import static com.github.dreamroute.mybatis.pro.core.consts.MapperLabel.NAMESPACE; 13 | import static java.nio.charset.StandardCharsets.UTF_8; 14 | 15 | /** 16 | * 描述:xml文件工具类 17 | * 18 | * @author w.dehi.2022-04-01 19 | */ 20 | public class XmlUtil { 21 | private XmlUtil() {} 22 | 23 | /** 24 | * 从mapper.xml文件中获取namespace 25 | * 26 | * @param resource mapper.xml数据流 27 | */ 28 | public static Class getNamespaceFromXmlResource(Resource resource) { 29 | try { 30 | XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, null, new XMLMapperEntityResolver()); 31 | XNode mapperNode = xPathParser.evalNode(MAPPER.getCode()); 32 | String namespace = mapperNode.getStringAttribute(NAMESPACE.getCode()); 33 | return loadClass(namespace); 34 | } catch (Exception e) { 35 | throw new MyBatisProException("解析mapper.xml文件获取namespace出错", e); 36 | } 37 | } 38 | 39 | /** 40 | * create a new and empty xml file of mapper. 41 | * 42 | * @param mapper namespace 43 | * @return return an empty xml file 44 | */ 45 | public static Resource createEmptyResource(Class mapper) { 46 | String namespace = mapper.getName(); 47 | String xml = 48 | "\n" + 49 | "\n" + 52 | ""; 53 | return new ByteArrayResource(xml.getBytes(UTF_8)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/KeyWord.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | /** 4 | * 方法命名的所有关键字,与JPA保持同步,去掉了JPA中个别有歧义以及雷同的用法的关键字 5 | * 参考:https://docs.spring.io/spring-data/jpa/docs/2.3.2.RELEASE/reference/html/# 6 | * 7 | * @author w.dehai 8 | */ 9 | public class KeyWord { 10 | 11 | private KeyWord() {} 12 | 13 | // … where x.lastname = ?1 and x.firstname = ?2 14 | public static final String AND = "And"; 15 | // … where x.lastname = ?1 or x.firstname = ?2 16 | public static final String OR = "Or"; 17 | 18 | // … where x.startDate between ?1 and ?2 19 | public static final String BETWEEN = "Between"; 20 | 21 | // … where x.age < ?1 22 | public static final String LESS_THAN = "LT"; 23 | // … where x.age <= ?1 24 | public static final String LESS_THAN_EQUAL = "LTE"; 25 | 26 | // … where x.age > ?1 27 | public static final String GREATER_THAN = "GT"; 28 | // … where x.age >= ?1 29 | public static final String GREATER_THAN_EQUAL = "GTE"; 30 | 31 | //… where x.age is null 32 | public static final String IS_NULL = "IsNull"; 33 | 34 | //… where x.age not null 35 | public static final String IS_NOT_NULL = "IsNotNull"; 36 | 37 | //… where x.age is null or x.age = '' 38 | public static final String IS_BLANK = "IsBlank"; 39 | 40 | //… where x.age is null or x.age = '' 41 | public static final String IS_NOT_BLANK= "IsNotBlank"; 42 | 43 | // … where x.firstname like ?1 44 | public static final String LIKE = "Like"; 45 | // … where x.firstname not like ?1 46 | public static final String NOT_LIKE = "NotLike"; 47 | 48 | // … where x.firstname like ?1% 49 | public static final String STARTING_WITH = "StartWith"; 50 | 51 | // … where x.firstname like %?1 52 | public static final String ENDING_WITH = "EndWith"; 53 | 54 | //… where x.lastname <> ?1 55 | public static final String NOT = "Not"; 56 | 57 | //… where x.age in ?1 58 | public static final String IN = "In"; 59 | // … where x.age not in ?1 60 | public static final String NOT_IN = "NotIn"; 61 | 62 | // ... 需要与order by配合 63 | public static final String DESC = "Desc"; 64 | // … where x.age = ?1 order by x.lastname( desc), 可以配合DESC常量 65 | public static final String ORDER_BY = "OrderBy"; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/consts/MyBatisProProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.consts; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | import static com.github.dreamroute.mybatis.pro.core.consts.MyBatisProProperties.DEFAULT_MYBATIS_PRO_PREFIX; 7 | 8 | /** 9 | * mybatis-pro的配置信息 10 | * 11 | * @author w.dehai 12 | */ 13 | @Data 14 | @ConfigurationProperties(prefix = DEFAULT_MYBATIS_PRO_PREFIX) 15 | public class MyBatisProProperties { 16 | 17 | public static final String DEFAULT_MYBATIS_PRO_PREFIX = "mybatis.pro"; 18 | 19 | /** 20 | * 逻辑删除备份表名: 21 | */ 22 | public static final String LOGICAL_DELETE_TABLE_NAME = "logical_delete"; 23 | /** 24 | * 逻辑删除状态列名 25 | */ 26 | public static final String LOGICAL_DELETE_STATUS_COLUMN = "status"; 27 | /** 28 | * 逻辑删除状态列有效值 29 | */ 30 | public static final Integer LOGICAL_DELETE_STATUS_ACTIVE = 1; 31 | /** 32 | * 逻辑删除状态列无效值 33 | */ 34 | public static final Integer LOGICAL_DELETE_STATUS_IN_ACTIVE = 0; 35 | 36 | /** 37 | * 是否使用{@link com.github.dreamroute.mybatis.pro.base.typehandler.EnumTypeHandler}这个枚举转换器 38 | */ 39 | private boolean enableEnumTypeHandler; 40 | 41 | /** 42 | * 是否开启逻辑删除,true-开启,false-关闭,默认关闭 43 | */ 44 | private boolean enableLogicalDelete; 45 | 46 | /** 47 | * 逻辑删除的方式:backup,update;backup:物理删除 + 备份;update:删除动作实际上是执行update操作,将状态位修改成为无效状态,默认是backup方式 48 | */ 49 | private LogicalDeleteType logicalDeleteType = LogicalDeleteType.BACKUP; 50 | 51 | /** 52 | * 逻辑删除备份表名,只有当logical-delete-type为backup时此属性才有意义 53 | */ 54 | private String logicalDeleteTable = LOGICAL_DELETE_TABLE_NAME; 55 | 56 | /** 57 | * 逻辑删除状态列,只有当logical-delete-type为update时此属性才有意义 58 | */ 59 | private String logicalDeleteColumn = LOGICAL_DELETE_STATUS_COLUMN; 60 | 61 | /** 62 | * 逻辑删除数据有效状态,单表查询时会在sql的where条件上自动加上[AND ${logicalDeleteColumn} = ${logicalDeleteActive}] 63 | */ 64 | private Integer logicalDeleteActive = LOGICAL_DELETE_STATUS_ACTIVE; 65 | 66 | /** 67 | * 逻辑删除数据删除状态,进行逻辑删除时,实际上执行的是[update set ${logicalDeleteColumn} = ${logicalDeleteInActive}] 68 | */ 69 | private Integer logicalDeleteInActive = LOGICAL_DELETE_STATUS_IN_ACTIVE; 70 | } 71 | -------------------------------------------------------------------------------- /mybatis-pro-interceptor/src/main/java/com/github/dreamroute/mybatis/pro/interceptor/ProxyUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 342252328@qq.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | package com.github.dreamroute.mybatis.pro.interceptor; 25 | 26 | import org.apache.ibatis.reflection.MetaObject; 27 | import org.apache.ibatis.reflection.SystemMetaObject; 28 | 29 | import java.lang.reflect.Proxy; 30 | 31 | /** 32 | * 获取代理对象的原始对象 33 | * 34 | * @author w.dehai 35 | */ 36 | public final class ProxyUtil { 37 | 38 | private ProxyUtil() {} // private constructor 39 | 40 | /** 41 | *

Recursive get the original target object. 42 | *

If integrate more than a plugin, maybe there are conflict in these plugins, because plugin will proxy the object.
43 | * So, here get the orignal target object 44 | * 45 | * @param target proxy-object 46 | * @return original target object 47 | */ 48 | public static T getOriginObj(T target) { 49 | if(Proxy.isProxyClass(target.getClass())) { 50 | MetaObject mo = SystemMetaObject.forObject(target); 51 | //noinspection unchecked 52 | return (T) getOriginObj(mo.getValue("h.target")); 53 | } 54 | return target; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro-samples 7 | ${revision} 8 | 9 | spring-boot 10 | ${project.artifactId} 11 | 12 | 13 | 14 | com.github.dreamroute 15 | mybatis-pro-boot-starter 16 | 17 | 18 | com.github.dreamroute 19 | sqlprinter-spring-boot-starter 20 | 2.3.3-RELEASE 21 | 22 | 23 | mybatis-pro-base 24 | com.github.dreamroute 25 | 26 | 27 | 28 | 29 | com.github.dreamroute 30 | pager-spring-boot-starter 31 | 2.4-RELEASE 32 | 33 | 34 | mybatis 35 | org.mybatis 36 | 37 | 38 | 39 | 40 | com.github.dreamroute 41 | hikari-spring-boot-starter 42 | 1.4-RELEASE 43 | 44 | 45 | 46 | com.baomidou 47 | mybatis-plus-boot-starter 48 | 3.5.3.1 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-maven-plugin 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/condition/CountByTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.condition; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import com.ninja_squad.dbsetup.operation.Insert; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | 14 | import static com.ninja_squad.dbsetup.Operations.insertInto; 15 | import static com.ninja_squad.dbsetup.Operations.truncate; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | 18 | /** 19 | * @author w.dehai 20 | */ 21 | @SpringBootTest 22 | class CountByTest { 23 | 24 | @Autowired 25 | private UserMapper userMapper; 26 | @Autowired 27 | private DataSource dataSource; 28 | 29 | @BeforeEach 30 | void init() { 31 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 32 | Insert insert = insertInto("smart_user") 33 | .columns("name", "password", "phone_no", "status") 34 | .values("w.dehai", "123456", "1306006", 1) 35 | .values("Jaedong", "123", "1306006", 1) 36 | .values("Jaedong", "123", "1352332", 1) 37 | .build(); 38 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 39 | } 40 | 41 | @Test 42 | void countByNameTest() { 43 | int result = userMapper.countByName("w.dehai"); 44 | assertEquals(1, result); 45 | } 46 | 47 | @Test 48 | void countByNameAndPhoneNo() { 49 | int result = userMapper.countByNameAndPhoneNo("w.dehai", "1306006"); 50 | assertEquals(1, result); 51 | } 52 | 53 | @Test 54 | void countByNameAndPhoneNoOptTest() { 55 | assertEquals(2, userMapper.countByNameAndPhoneNoOpt("", "1306006")); 56 | assertEquals(2, userMapper.countByNameAndPhoneNoOpt(null, "1306006")); 57 | assertEquals(3, userMapper.countByNameAndPhoneNoOpt("", "")); 58 | assertEquals(3, userMapper.countByNameAndPhoneNoOpt(null, null)); 59 | assertEquals(3, userMapper.countByNameAndPhoneNoOpt(null, "")); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/typehandler/EnumTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.typehandler; 2 | 3 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarker; 4 | import org.apache.ibatis.type.BaseTypeHandler; 5 | import org.apache.ibatis.type.JdbcType; 6 | 7 | import java.sql.CallableStatement; 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | 12 | /** 13 | * 枚举type handler处理器 14 | * 15 | * @author w.dehai 16 | */ 17 | public class EnumTypeHandler extends BaseTypeHandler { 18 | 19 | private Class type; 20 | 21 | public EnumTypeHandler() {} 22 | 23 | public EnumTypeHandler(Class type) { 24 | if (type == null) { 25 | throw new IllegalArgumentException("Type argument cannot be null"); 26 | } 27 | this.type = type; 28 | } 29 | 30 | @Override 31 | public void setNonNullParameter(PreparedStatement ps, int i, EnumMarker parameter, JdbcType jdbcType) throws SQLException { 32 | ps.setObject(i, parameter.getValue()); 33 | } 34 | 35 | @Override 36 | public E getNullableResult(ResultSet rs, String columnName) throws SQLException { 37 | int value = rs.getInt(columnName); 38 | if (rs.wasNull()) { 39 | return null; 40 | } else { 41 | try { 42 | return EnumMarker.valueOf(type, value); 43 | } catch (Exception ex) { 44 | throw new IllegalArgumentException("Cannot convert " + value + " to " + type.getSimpleName() + " by ordinal value.", ex); 45 | } 46 | } 47 | } 48 | 49 | @Override 50 | public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { 51 | int value = rs.getInt(columnIndex); 52 | if (rs.wasNull()) { 53 | return null; 54 | } else { 55 | try { 56 | return EnumMarker.valueOf(type, value); 57 | } catch (Exception ex) { 58 | throw new IllegalArgumentException("Cannot convert " + value + " to " + type.getSimpleName() + " by ordinal value.", ex); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { 65 | throw new IllegalArgumentException("MyBatisPro enum typehandler not support CallableStatement."); 66 | } 67 | } -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/sdk/UpdateTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.sdk; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import com.ninja_squad.dbsetup.operation.Insert; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import javax.sql.DataSource; 14 | 15 | import static com.ninja_squad.dbsetup.Operations.insertInto; 16 | import static com.ninja_squad.dbsetup.Operations.truncate; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.assertNull; 19 | 20 | /** 21 | * @author w.dehai 22 | */ 23 | @SpringBootTest 24 | class UpdateTest { 25 | 26 | @Autowired 27 | private UserMapper userMapper; 28 | @Autowired 29 | private DataSource dataSource; 30 | 31 | @BeforeEach 32 | void init() { 33 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 34 | Insert insert = insertInto("smart_user") 35 | .columns("name", "status") 36 | .values("w.dehai", 1).build(); 37 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 38 | } 39 | 40 | @Test 41 | void updateByIdTest() { 42 | User user = userMapper.selectById(1L); 43 | user.setName("Jaedong"); 44 | user.setVersion(1L); 45 | user.setPassword(null); 46 | userMapper.updateById(user); 47 | 48 | User result = userMapper.selectById(1L); 49 | assertEquals("Jaedong", result.getName()); 50 | assertNull(result.getPassword()); 51 | } 52 | 53 | @Test 54 | void updateByIdExcludeNull() { 55 | User user = userMapper.selectById(1L); 56 | user.setName("Jaedong"); 57 | user.setVersion(1L); 58 | user.setPassword(null); 59 | int cols = userMapper.updateByIdExcludeNull(user); 60 | assertEquals(1, cols); 61 | 62 | User result = userMapper.selectById(1L); 63 | assertEquals("Jaedong", result.getName()); 64 | assertEquals("123456", result.getPassword()); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/util/ClassPathUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.core.io.Resource; 5 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 6 | import org.springframework.core.io.support.ResourcePatternResolver; 7 | import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 8 | import org.springframework.core.type.classreading.MetadataReader; 9 | import org.springframework.core.type.classreading.MetadataReaderFactory; 10 | import org.springframework.util.ClassUtils; 11 | 12 | import java.io.File; 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | /** 17 | * 描述:解析路径通配符 18 | * 19 | * @author w.dehi.2022-02-28 20 | */ 21 | @Slf4j 22 | public class ClassPathUtil { 23 | private ClassPathUtil() {} 24 | 25 | public static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; 26 | 27 | /** 28 | * 获取指定路径下的所有包名 29 | * 30 | * @param packagePath 指定路径 31 | * @return 返回该路径下的所有包名 32 | */ 33 | public static String[] resolvePackage(String packagePath) { 34 | // 资源路径解析器 35 | ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 36 | // 元数据读取 37 | MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); 38 | // 解析路径 39 | packagePath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(packagePath) + File.separator + DEFAULT_RESOURCE_PATTERN; 40 | // 别名包路径集合 41 | Set result = new HashSet<>(); 42 | try { 43 | // 根据路径 读取所有的类资源 44 | Resource[] resources = resolver.getResources(packagePath); 45 | if (resources.length > 0) { 46 | MetadataReader metadataReader; 47 | for (Resource resource : resources) { 48 | if (resource.isReadable()) { 49 | // 读取类的信息,每个 Resource 都是一个类资源 50 | metadataReader = metadataReaderFactory.getMetadataReader(resource); 51 | result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); 52 | } 53 | } 54 | } 55 | } catch (Exception e) { 56 | throw new IllegalArgumentException("解析带有通配符的路径失败, 路径信息为: " + packagePath, e); 57 | } 58 | return result.toArray(new String[0]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /mybatis-pro-service/src/main/java/com/github/dreamroute/mybatis/pro/service/service/AbstractServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.service.service; 2 | 3 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 4 | import com.google.common.collect.Lists; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.transaction.annotation.Transactional; 7 | import org.springframework.util.CollectionUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author w.dehai 13 | */ 14 | public class AbstractServiceImpl implements BaseService { 15 | 16 | @Autowired 17 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") 18 | private BaseMapper mapper; 19 | 20 | @Override 21 | @Transactional 22 | public T insert(T entity) { 23 | mapper.insert(entity); 24 | return entity; 25 | } 26 | 27 | @Override 28 | @Transactional 29 | public T insertExcludeNull(T entity) { 30 | mapper.insertExcludeNull(entity); 31 | return entity; 32 | } 33 | 34 | @Override 35 | @Transactional 36 | public List insertList(List entityList) { 37 | return insertList(entityList, 10); 38 | } 39 | 40 | @Override 41 | public List insertList(List entityList, int partition) { 42 | if (!CollectionUtils.isEmpty(entityList)) { 43 | List> result = Lists.partition(entityList, partition); 44 | result.forEach(mapper::insertList); 45 | } 46 | return entityList; 47 | } 48 | 49 | @Override 50 | @Transactional 51 | public int delete(ID id) { 52 | return mapper.deleteById(id); 53 | } 54 | 55 | @Override 56 | @Transactional 57 | public int delete(List ids) { 58 | return mapper.deleteByIds(ids); 59 | } 60 | 61 | @Override 62 | @Transactional 63 | public int update(T entity) { 64 | return mapper.updateById(entity); 65 | } 66 | 67 | @Override 68 | @Transactional 69 | public int updateExcludeNull(T entity) { 70 | return mapper.updateByIdExcludeNull(entity); 71 | } 72 | 73 | @Override 74 | public T selectById(ID id, String... cols) { 75 | return mapper.selectById(id, cols); 76 | } 77 | 78 | @Override 79 | public List selectByIds(List ids, String... cols) { 80 | return mapper.selectByIds(ids, cols); 81 | } 82 | 83 | @Override 84 | public List selectAll(String... cols) { 85 | return mapper.selectAll(cols); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/interceptor/LockerInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.interceptor; 2 | 3 | import com.github.dreamroute.locker.anno.EnableLocker; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 5 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 6 | import com.ninja_squad.dbsetup.DbSetup; 7 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 8 | import com.ninja_squad.dbsetup.operation.Insert; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import javax.annotation.Resource; 14 | import javax.sql.DataSource; 15 | 16 | import static com.ninja_squad.dbsetup.Operations.insertInto; 17 | import static com.ninja_squad.dbsetup.Operations.truncate; 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | /** 21 | * 描述:乐观锁插件测试,测试时在启动类上添加@EnableLocker,观察SQL是否是带有乐观锁 22 | * 23 | * @author w.dehi.2022-02-17 24 | */ 25 | @EnableLocker 26 | @SpringBootTest 27 | class LockerInterceptorTest { 28 | @Resource 29 | private UserMapper userMapper; 30 | @Resource 31 | private DataSource dataSource; 32 | 33 | @BeforeEach 34 | void init() { 35 | // init smat_user 36 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 37 | Insert insert = insertInto("smart_user") 38 | .columns("name", "password", "phone_no", "version", "addr_info", "status") 39 | .values("w.dehai", "123456", "1306006", 1L, "成都", 1) 40 | .values("Jaedong", "123", "1306006", 1L, "北京", 1) 41 | .values("w.dehai", "123", "1306006", 2L, "美国", 1) 42 | .build(); 43 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 44 | } 45 | 46 | @Test 47 | void updateTest() { 48 | User user = userMapper.selectById(1L); 49 | user.setPassword("dd"); 50 | int update = userMapper.updateById(user); 51 | assertEquals(1, update); 52 | User result = userMapper.selectById(1L); 53 | assertEquals("dd", result.getPassword()); 54 | } 55 | 56 | @Test 57 | void updateExcludeNullTest() { 58 | User user = userMapper.selectById(1L); 59 | user.setPassword("dd"); 60 | int update = userMapper.updateByIdExcludeNull(user); 61 | assertEquals(1, update); 62 | User result = userMapper.selectById(1L); 63 | assertEquals("dd", result.getPassword()); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/sdk/SelectTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.sdk; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import com.ninja_squad.dbsetup.operation.Insert; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import javax.sql.DataSource; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | import static com.ninja_squad.dbsetup.Operations.insertInto; 18 | import static com.ninja_squad.dbsetup.Operations.truncate; 19 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | /** 23 | * @author w.dehai 24 | */ 25 | @SpringBootTest 26 | class SelectTest { 27 | 28 | @Autowired 29 | private UserMapper userMapper; 30 | @Autowired 31 | private DataSource dataSource; 32 | 33 | @BeforeEach 34 | void init() { 35 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 36 | Insert insert = insertInto("smart_user") 37 | .columns("name", "status") 38 | .values("w.dehai", 1) 39 | .values("Jaedong", 1) 40 | .values("Dreamroute", 1) 41 | .build(); 42 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 43 | } 44 | 45 | @Test 46 | void selectByIdTest() { 47 | User user = userMapper.selectById(1L); 48 | assertEquals(1L, user.getId()); 49 | } 50 | 51 | @Test 52 | void selectByIdsTest() { 53 | List users = userMapper.selectByIds(Arrays.asList(2L, 3L)); 54 | Long[] ids = users.stream().map(User::getId).toArray(Long[]::new); 55 | assertArrayEquals(new Long[]{2L, 3L}, ids); 56 | } 57 | 58 | @Test 59 | void selectAllTest() { 60 | List users = userMapper.selectAll(); 61 | Long[] ids = users.stream().map(User::getId).toArray(Long[]::new); 62 | assertArrayEquals(new Long[]{1L, 2L, 3L}, ids); 63 | } 64 | 65 | @Test 66 | void selectByNameAndPasswordTest() { 67 | List users = userMapper.selectByNameAndPasssword("w.dehai", "123456"); 68 | System.err.println(users); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/test/java/com/github/dreamroute/mybatis/pro/core/ClassUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Transient; 4 | import com.github.dreamroute.mybatis.pro.core.util.ClassUtil; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.io.Serializable; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.Method; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.stream.Collectors; 15 | 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | 18 | /** 19 | * @author w.dehai 20 | */ 21 | class ClassUtilTest { 22 | 23 | @Test 24 | void getName2TypeTest() { 25 | Map name2Type = ClassUtil.getMethodName2ReturnType(DemoMapper.class); 26 | assertEquals(5, name2Type.size()); 27 | } 28 | 29 | @Test 30 | void getReturnTypeTest() throws NoSuchMethodException, SecurityException { 31 | 32 | // 返回值为entity 33 | Method method1 = DemoMapper.class.getDeclaredMethod("findByNameAndPassword", String.class, String.class); 34 | // 返回值为List 35 | Method method2 = DemoMapper.class.getDeclaredMethod("findByName", String.class); 36 | 37 | String type1 = ClassUtil.getReturnType(method1); 38 | String type2 = ClassUtil.getReturnType(method2); 39 | 40 | assertEquals("com.github.dreamroute.mybatis.pro.core.Demo", type1); 41 | assertEquals("com.github.dreamroute.mybatis.pro.core.Demo", type2); 42 | } 43 | 44 | @Test 45 | void getSpecialMethodsTest() { 46 | List names = ClassUtil.getSpecialMethods(DemoMapper.class); 47 | String result = names.stream().sorted().collect(Collectors.joining(",", "[", "]")); 48 | assertEquals("[findById,findByName,findByNameAndPassword]", result); 49 | } 50 | 51 | @Test 52 | void getAllFieldsTest() { 53 | Set allFields = ClassUtil.getAllFields(User.class); 54 | assertEquals(1, allFields.size()); 55 | } 56 | 57 | @Test 58 | void getInnerMethodNamesTest() { 59 | Set methodNames = ClassUtil.getBaseMethodNames(); 60 | Assertions.assertNotNull(methodNames); 61 | } 62 | 63 | } 64 | 65 | class M { 66 | @Transient 67 | private String name; 68 | 69 | public String getName() { 70 | return name; 71 | } 72 | 73 | public void setName(String name) { 74 | this.name = name; 75 | } 76 | } 77 | 78 | class User extends M implements Serializable { 79 | private static final long serialVersionUID = -1383742108573524072L; 80 | private Long id; 81 | 82 | public Long getId() { 83 | return id; 84 | } 85 | 86 | void setId(Long id) { 87 | this.id = id; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/EnumMarkerDeserializerForCollection.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonDeserializer; 7 | import com.fasterxml.jackson.databind.JsonNode; 8 | import com.fasterxml.jackson.databind.node.ArrayNode; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.ParameterizedType; 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.Iterator; 16 | 17 | /** 18 | * 描述:EnumMarker Jackson列表反序列化,DTO如果需要使用列表枚举,需要定义成Collection类型, 19 | * 如果这里使用{@link java.util.Set}或者{@link java.util.List}作为泛型,那么除了{@link EnumMarker}之外的数组、列表类型也会进入此方法, 20 | * 所以必须使用Collection,在DTO中的定义如下: 21 | *

22 |  *     @Data
23 |  *     public class DemoDto {
24 |  *
25 |  *         // 使用此方式
26 |  *         private Collection<Gender> genders;
27 |  *
28 |  *         // 不要使用此方式
29 |  *         private List<Gender> genders;
30 |  *
31 |  *     }
32 |  * 
33 | * 34 | * @author w.dehi.2021-12-19 35 | */ 36 | public class EnumMarkerDeserializerForCollection extends JsonDeserializer>> { 37 | 38 | @SuppressWarnings("unchecked") 39 | @Override 40 | public Collection> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 41 | ArrayNode treeNode = p.readValueAsTree(); 42 | Field field; 43 | try { 44 | field = p.getCurrentValue().getClass().getDeclaredField(p.currentName()); 45 | } catch (NoSuchFieldException e) { 46 | return null; 47 | } 48 | field.setAccessible(true); 49 | if (!field.getType().equals(Collection.class)) { 50 | return null; 51 | } 52 | ParameterizedType genericType = (ParameterizedType) field.getGenericType(); 53 | Class actualTypeArgument = (Class) genericType.getActualTypeArguments()[0]; 54 | @SuppressWarnings("rawtypes") Collection result = new ArrayList<>(); 55 | Iterator elements = treeNode.elements(); 56 | while (elements.hasNext()) { 57 | String v = elements.next().asText(); 58 | if (EnumMarker.class.isAssignableFrom(actualTypeArgument)) { 59 | EnumMarker enumMarker = EnumMarker.valueOf(actualTypeArgument, Integer.parseInt(v)); 60 | result.add(enumMarker); 61 | } else { 62 | result.add(v); 63 | } 64 | } 65 | return result; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * 描述:此包是枚举EnumMarker的序列化和反序列化的jackson实现, 4 | *

5 | * JacksonSerializer的枚举类型可以是EnumMarker,也可以是Enum类型, 6 | *

7 | * 但是JacksonDeserializer的枚举类型就不能是EnumMarker,必须是Enum类型或者是具体的枚举类型, 8 | *

9 | * 开发JacksonDeserializer的灵感来源于这里:https://www.cnblogs.com/kelelipeng/p/13972138.html, 10 | *

11 | * 事实上也可以使用Fastjson方式的序列化和反序列化方式,但是Fastjson在前些年出过几次比较重大的漏洞时间,加上Fastjson源码不够优秀,并且基本上是个人开发者,而jackson是Spring默认的序列化和反序列化工具包。 12 | *

13 | * 使用方式: 14 | * 1. 可以自定义HttpMessageConverter实现WebMvcConfigurer的configureMessageConverters方法,例如: 15 | *

16 |  * @Configuration
17 |  * public class HttpMsgConverterConfig implements WebMvcConfigurer {
18 |  *     @Override
19 |  *     public void configureMessageConverters(@Nonnull List> converters) {
20 |  *         converters.removeIf(e -> e instanceof MappingJackson2HttpMessageConverter);
21 |  *
22 |  *         SimpleModule module = new SimpleModule();
23 |  *         module.addSerializer(Enum.class, new JacksonSerializer());
24 |  *         module.addDeserializer(Enum.class, new JacksonDeserializer());
25 |  *         simpleModule.addDeserializer(Collection.class, new EnumMarkerDeserializerForCollection());
26 |  *
27 |  *         ObjectMapper om = new ObjectMapper();
28 |  *         om.registerModule(module);
29 |  *
30 |  *         MappingJackson2HttpMessageConverter c = new MappingJackson2HttpMessageConverter(om);
31 |  *         c.setObjectMapper(om);
32 |  *
33 |  *         converters.add(0, c);
34 |  *     }
35 |  * }
36 |  * 
37 | * 2. 也可以不自定义HttpMessageConverter,直接在EnumMarker头顶上加上序列化和反序列化的注解,例如: 38 | *
39 |  * @JsonSerialize(using = JacksonSerializer.class)
40 |  * @JsonDeserialize(using = JacksonDeserializer.class)
41 |  * public interface EnumMarker extends Serializable {}
42 |  * 
43 | * 44 | * 3. 对于枚举类型fastjson和jackson选型问题,fastjson存在的问题: 45 | * 1. 列表方式的枚举反序列化会oom; 46 | * 2. 枚举类型传null会有默认认知0造成业务出错,业务中如果允许Gender为空,前端传{"gender": null},那么gender就是0对应的枚举,而jackson就不会 47 | *
48 |  *     public class Demo {
49 |  *         private Gender gender;
50 |  *     }
51 |  * 
52 | * 3. 并且通过{@link com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerDeserializerForCollection}能够解决列表方式的枚举问题,而fastjson的oom就不太好解决 53 | * 4. 使用Jackson方式反序列化需要与前端约定:对于空对象,要么不传,要么传null,不能传空括号,如下json请求不允许: 54 | *
55 |  *     {
56 |  *         "user": {}
57 |  *         "birthday": "1990-05-03"
58 |  *     }
59 |  * 
60 | * 因为这样的话,在自定义反序列化对象中,比如EnumMarkerDeserializer、EnumMarkerDeserializerForCollection、DateDeserializer的p.getCurrentValue()会读取错位,birthday会读取到User.class 61 | * @author w.dehi.2021-12-19 62 | */ 63 | package com.github.dreamroute.mybatis.pro.base.codec.enums; -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/sdk/InsertTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.sdk; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 5 | import com.ninja_squad.dbsetup.DbSetup; 6 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import javax.sql.DataSource; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Objects; 16 | 17 | import static com.ninja_squad.dbsetup.Operations.truncate; 18 | import static java.util.stream.Collectors.toList; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | /** 22 | * @author w.dehai 23 | */ 24 | @SpringBootTest 25 | class InsertTest { 26 | 27 | @Autowired 28 | private UserMapper userMapper; 29 | @Autowired 30 | private DataSource dataSource; 31 | 32 | @BeforeEach 33 | void init() { 34 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 35 | } 36 | 37 | @Test 38 | void insertTest() { 39 | User user = User.builder().name("w.dehai").password("123456").version(1L).phoneNo("1306006").status(1).build(); 40 | userMapper.insert(user); 41 | assertEquals(1, userMapper.selectAll().size()); 42 | } 43 | 44 | @Test 45 | void insertExcludeNullTest() { 46 | User user = User.builder().name("w.dehai").version(1L).status(1).build(); 47 | userMapper.insertExcludeNull(user); 48 | List users = userMapper.selectAll(); 49 | assertEquals("123456", users.get(0).getPassword()); 50 | } 51 | 52 | @Test 53 | void insertListTest() { 54 | int size = 3; 55 | List users = new ArrayList<>(3); 56 | for (int i = 0; i < size; i++) { 57 | User user = User.builder().name("w.dehai").password("123456").version(1L).phoneNo("1306006").build(); 58 | users.add(user); 59 | } 60 | userMapper.insertList(users); 61 | List ids = users.stream().map(User::getId).filter(Objects::nonNull).collect(toList()); 62 | assertEquals(size, ids.size()); 63 | } 64 | 65 | @Test 66 | void insertWithIdTest() { 67 | User user = new User(); 68 | user.setId(1000L); 69 | user.setName("w.dehai"); 70 | userMapper.insert(user); 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 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/controller/DemoControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.controller; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Demo; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.web.servlet.MockMvc; 8 | 9 | import javax.annotation.Resource; 10 | 11 | import static com.github.dreamroute.mybatis.pro.sample.springboot.domain.Gender.FEMALE; 12 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 13 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 15 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 16 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 17 | 18 | /** 19 | * 描述:枚举类型的序列化和反序列化测试 20 | * 21 | * @author w.dehi.2021-12-20 22 | */ 23 | @SpringBootTest 24 | @AutoConfigureMockMvc 25 | class DemoControllerTest { 26 | 27 | private static final String BIRTHDAY_STR = "2022-05-05 15:05:12"; 28 | 29 | @Resource 30 | private MockMvc mockMvc; 31 | 32 | @Test 33 | void enumTest() throws Exception { 34 | Demo demo = new Demo(); 35 | demo.setGender(FEMALE); 36 | 37 | String request = "{\"gender\": 2}"; 38 | mockMvc.perform(post("/demo/enumTest").content(request).contentType(APPLICATION_JSON_VALUE)) 39 | .andDo(print()) 40 | .andExpect(status().isOk()) 41 | .andExpect(jsonPath("$.gender.desc").value("女")); 42 | } 43 | 44 | /** 45 | * 数组类型的枚举和日期反序列化 46 | */ 47 | @Test 48 | void arrTest() throws Exception { 49 | String request = "{\n" + 50 | " \"demos\": [\n" + 51 | " {\n" + 52 | " \"gender\": 1,\n" + 53 | " \"birthday\": \"" + BIRTHDAY_STR + "\"\n" + 54 | " },\n" + 55 | " {\n" + 56 | " \"gender\": 2,\n" + 57 | " \"birthday\": \"" + BIRTHDAY_STR + "\"\n" + 58 | " }\n" + 59 | " ]\n" + 60 | "}"; 61 | mockMvc.perform(post("/demo/arrTest").content(request).contentType(APPLICATION_JSON_VALUE)) 62 | .andDo(print()) 63 | .andExpect(status().isOk()) 64 | .andExpect(jsonPath("$.demos[0].gender.desc").value("男")) 65 | .andExpect(jsonPath("$.demos[1].gender.desc").value("女")) 66 | .andExpect(jsonPath("$.demos[0].birthday").value(BIRTHDAY_STR)) 67 | .andExpect(jsonPath("$.demos[1].birthday").value(BIRTHDAY_STR)); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/util/DictUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.util; 2 | 3 | import cn.hutool.core.util.ReflectUtil; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import static com.github.dreamroute.mybatis.pro.sample.springboot.util.DictFieldCache.findForClass; 13 | import static java.util.stream.Collectors.groupingBy; 14 | import static java.util.stream.Collectors.toList; 15 | import static java.util.stream.Collectors.toMap; 16 | 17 | /** 18 | * 字典工具类 19 | * 20 | * @author w.dehai 21 | */ 22 | public final class DictUtil { 23 | 24 | private DictUtil() {} 25 | 26 | /** 27 | * 字典List -> Map> 28 | */ 29 | public static Map> dict2Map(List list) { 30 | Map> result = new HashMap<>(128); 31 | list.stream().collect(groupingBy(Dict::getEnName)) 32 | .forEach((k, v) -> result.put(k, v.stream().collect(toMap(Dict::getValue, Dict::getLabelValue)))); 33 | return result; 34 | } 35 | 36 | /** 37 | * 将带有@Dict标记的字段进行翻译:[state = 0] -> [stateDict = '有效'],无需手动对每个字典字段进行值的设置 38 | */ 39 | @SuppressWarnings("unchecked") 40 | public static K convert(K src, Map> dictMap) { 41 | Class c = src.getClass(); 42 | if (Collection.class.isAssignableFrom(c)) { 43 | Collection cs = (Collection) src; 44 | return (K) cs.stream().map(sr -> convertSingle(sr, dictMap)).collect(toList()); 45 | } 46 | return convertSingle(src, dictMap); 47 | } 48 | 49 | private static K convertSingle(K src, Map> dictMap) { 50 | Class c = src.getClass(); 51 | List fields = findForClass(c); 52 | for (Field field : fields) { 53 | Object value = ReflectUtil.getFieldValue(src, field); 54 | com.github.dreamroute.mybatis.pro.sample.springboot.util.anno.Dict d = field.getAnnotation(com.github.dreamroute.mybatis.pro.sample.springboot.util.anno.Dict.class); 55 | String lineName = d.value(); 56 | if (lineName.length() == 0) { 57 | lineName = humpToLine(field.getName()); 58 | } 59 | Map nameMap = dictMap.get(lineName); 60 | String v = nameMap.get(value); 61 | Field prop = ReflectUtil.getField(c, field.getName() + "Dict"); 62 | ReflectUtil.setFieldValue(src, prop, v); 63 | } 64 | return src; 65 | } 66 | 67 | /** 68 | * 驼峰转下划线 69 | * 70 | * @param src 驼峰 71 | * @return 返回下划线名称 72 | */ 73 | public static String humpToLine(String src) { 74 | return src.replaceAll("[A-Z]", "_$0").toLowerCase(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyBatis-Pro,持久化框架最终兵器 2 | ![img](https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/Wmeona977bbQnXxj/img/696fcaff-4228-45cd-9f1f-12573108eeb9.png) 3 | ### **大道至简,优雅解决MyBatis单表的一切问题** 4 | 5 | ## **源码地址** 6 | - [GitHub](https://github.com/Dreamroute/mybatis-pro.git) 7 | - [码云](https://gitee.com/Dreamroute/mybatis-pro) 8 | 9 | ## 使用文档 10 | 11 | - [完整中文文档](https://github.com/Dreamroute/mybatis-pro/wiki/MyBatis-Pro%EF%BC%8C%E6%8C%81%E4%B9%85%E5%8C%96%E6%A1%86%E6%9E%B6%E6%9C%80%E7%BB%88%E5%85%B5%E5%99%A8) 12 | 13 | ## **开发此框架的初衷** 14 | - 让单表查询更加优雅 15 | - 基本告别单表SQL语句 16 | - 媲美JPA的单表查询优势 17 | - 拒绝重复造轮子 18 | - 钢铁直男的终极选择 19 | 20 | ## 框架功能 21 | 22 | - 无需手动编写单表增删改查方法,框架自动生成 23 | 24 | - 与通用Mapper、MyBatis-Plus等三方框架兼容(三者选其一即可,功能类似) 25 | 26 | - 【可选】内置枚举类型处理器,优雅解决枚举类型问题,无需手动转换 27 | 28 | - 【可选】内置泛型Service,避免重复造轮子编写大量类似的Service方法代码 29 | 30 | - 【可选】内置两种方式逻辑删除,可放心大胆的在生产环境进行delete操作,不用担心误删数据 31 | 32 | - 【可选】[分页插件](https://github.com/Dreamroute/pager)支持单表、多表关联查询、支持复杂的多表分页查询 33 | 34 | - 【可选】[sql打印插件](https://github.com/Dreamroute/sqlprinter)已经用实际参数替换了?占位符,可以从日志文件拷贝出来直接执行 35 | 36 | - 【可选】[乐观锁插件](https://github.com/Dreamroute/locker)透明解决乐观锁问题 37 | 38 | 39 | ## 设计原则 40 | 框架本身依赖mybatis-spring,仅在应用启动时织入框架逻辑,不破坏任何mybatis核心,原则上可以兼容任何mybatis版本 41 | 42 | ## 版本要求 43 | - JDK 1.8+ 44 | - maven-compiler-plugin编译插件不要关闭`-parameters`,因为需要通过反射获取参数名称(这是jdk8的特性) 45 | 46 | ## 使用方式 47 | - SpringBoot 48 | ```xml 49 | 50 | com.github.dreamroute 51 | mybatis-pro-boot-starter 52 | latest version 53 | 54 | ``` 55 | ### 最新版本:[点击查看](https://search.maven.org/artifact/com.github.dreamroute/mybatis-pro-boot-starter) 56 | 57 | ## 功能展示 58 | 59 | - 单表条件查询 60 | ```java 61 | public interface UserMapper { 62 | 63 | // 这是一个根据用户名、密码查询单个用户的查询,方法名只需要以findBy打头,接着方法名为: NameAndPassword 64 | User findByNameAndPasswordLimit(String name, String password, Integer limit); 65 | 66 | } 67 | ``` 68 | 你无需在xml文件中编写sql,也无需使用注解@Select("xxx")的sql,框架自动根据方法名: 69 | 70 | **findByNameAndPassword**切割成`findBy`,`Name`, `And`, `Password`,组成如下sql: 71 | 72 | `select * from user where name = #{name} and password = #{password} limit #{limit}` 73 | 74 | ## 对比(mybatis-plus、通用mapper) 75 | > **需求:查询字段version字段大小在2~4之间,并且根据id反向排序** 76 | 77 | - mybatis-pro: 78 | ```java 79 | @Test 80 | void proTest() { 81 | List users = findByVersionBetweenOrderByIdDesc(2L, 4L); 82 | } 83 | ``` 84 | - mybatis-plus: 85 | ``` 86 | @Test 87 | void plusTest() { 88 | LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); 89 | LambdaQueryWrapper query = qw.between(User::getVersion, 2L, 4L); 90 | query.orderByDesc(User::getId); 91 | List users = userMapper.selectList(query); 92 | } 93 | ``` 94 | 95 | - 通用mapper 96 | ``` 97 | @Test 98 | void mapperTest() { 99 | Example e = new Example(User.class); 100 | e.orderBy("id").desc(); 101 | Criteria criteria = e.createCriteria().andBetween("version", 2L, 4L); 102 | List users = userMapper.selectByExample(e); 103 | } 104 | ``` 105 | 106 | ## 全部文档 107 | 108 | - [完整中文文档](https://github.com/Dreamroute/mybatis-pro/wiki/MyBatis-Pro%EF%BC%8C%E6%8C%81%E4%B9%85%E5%8C%96%E6%A1%86%E6%9E%B6%E6%9C%80%E7%BB%88%E5%85%B5%E5%99%A8) 109 | 110 | ## 作者信息 111 | Email: 342252328@qq.com 112 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/controller/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.controller; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Demo; 5 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Gender; 6 | import lombok.Data; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.validation.Valid; 13 | import java.io.Serializable; 14 | import java.time.LocalDate; 15 | import java.time.LocalDateTime; 16 | import java.util.Date; 17 | import java.util.List; 18 | 19 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 20 | 21 | /** 22 | * 描述:测试枚举类型的HttpMessageConverter 23 | * 24 | * @author w.dehi.2021-12-20 25 | */ 26 | @RestController 27 | @RequestMapping(produces = {APPLICATION_JSON_VALUE}) 28 | public class DemoController { 29 | 30 | /** 31 | * 测试枚举的序列化和反序列化(包括嵌套类型) 32 | * 33 | * @return 返回值中带有枚举类型的序列化 34 | */ 35 | @PostMapping("/demo/enumTest") 36 | EnumDto enumTest(@RequestBody @Valid EnumDto enumDto) { 37 | return enumDto; 38 | } 39 | 40 | @PostMapping("/demo/arrTest") 41 | DemoDTO arr(@RequestBody DemoDTO demoDTO) { 42 | return demoDTO; 43 | } 44 | 45 | @PostMapping("/demo/deserializeEnumArrTest") 46 | DeserializeEnumDTO deserializeEnumArrTest(@RequestBody DeserializeEnumDTO deserializeEnumDTO) { 47 | System.err.println(deserializeEnumDTO); 48 | return deserializeEnumDTO; 49 | } 50 | 51 | @PostMapping("/demo/deserializeDateTest") 52 | DeserializeDateDTO deserializeDateTest(@RequestBody DeserializeDateDTO req) { 53 | System.err.println(req); 54 | return req; 55 | } 56 | 57 | @Data 58 | public static class DemoDTO implements Serializable { 59 | private List demos; 60 | } 61 | 62 | @Data 63 | public static class DeserializeEnumDTO implements Serializable { 64 | // private Collection genders; 65 | private List names; 66 | private Gender gender; 67 | private List subs; 68 | private Date birthday; 69 | private String[] roles; 70 | } 71 | 72 | @Data 73 | public static class DeserializeDateDTO implements Serializable { 74 | private Date date; 75 | private LocalDate localDate; 76 | private LocalDateTime localDateTime; 77 | @JsonProperty("user_name") 78 | private String userName; 79 | } 80 | 81 | @Data 82 | public static class Sub implements Serializable { 83 | private Long id; 84 | private String name; 85 | private Gender gender; 86 | } 87 | 88 | @Data 89 | public static class EnumDto implements Serializable { 90 | private Long id; 91 | private Gender gender; 92 | private EnumNestDto enumNestDto; 93 | } 94 | 95 | @Data 96 | public static class EnumNestDto implements Serializable { 97 | private Long id; 98 | private Gender gender; 99 | private EnumNestDto inner; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/condition/LimitTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.condition; 2 | 3 | import com.github.dreamroute.common.util.test.Appender; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 5 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 6 | import com.github.dreamroute.sqlprinter.starter.interceptor.SqlPrinter; 7 | import com.ninja_squad.dbsetup.DbSetup; 8 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 9 | import com.ninja_squad.dbsetup.operation.Insert; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | import javax.sql.DataSource; 16 | import java.util.List; 17 | 18 | import static com.ninja_squad.dbsetup.Operations.insertInto; 19 | import static com.ninja_squad.dbsetup.Operations.truncate; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | /** 24 | * @author w.dehai 25 | */ 26 | @SpringBootTest 27 | class LimitTest { 28 | 29 | @Autowired 30 | private UserMapper userMapper; 31 | @Autowired 32 | private DataSource dataSource; 33 | 34 | @BeforeEach 35 | void init() { 36 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 37 | Insert insert = insertInto("smart_user") 38 | .columns("name", "password", "phone_no", "version", "status") 39 | .values("w.dehai", "123456", "1306006", 1L, 1) 40 | .values("Jaedong", "123", "1306006", 1L, 1) 41 | .values("w.dehai", "123", "1306006", 2L, 1) 42 | .values("w.dehai", "123", "1306006", 2L, 1) 43 | .build(); 44 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 45 | } 46 | 47 | @Test 48 | void findByNameLimitTest() { 49 | Appender appender = new Appender(SqlPrinter.class); 50 | List result = userMapper.findByNameLimit("w.dehai", 2); 51 | assertEquals(2, result.size()); 52 | assertTrue(appender.contains( 53 | "SELECT password, order_id AS orderId, name, id, addr_info AS addr\n" + 54 | "\t, version, phone_no AS phoneNo, status\n" + 55 | "FROM smart_user\n" + 56 | "WHERE name = 'w.dehai'\n" + 57 | "LIMIT 2") 58 | ); 59 | } 60 | 61 | @Test 62 | void findByNameAndPasswordOrderByIdLimitTest() { 63 | Appender appender = new Appender(SqlPrinter.class); 64 | List users = userMapper.findByNameAndPasswordOrderByIdLimit("w.dehai", "123", 2); 65 | assertEquals(2, users.size()); 66 | assertTrue(appender.contains( 67 | "SELECT password, order_id AS orderId, name, id, addr_info AS addr\n" + 68 | "\t, version, phone_no AS phoneNo, status\n" + 69 | "FROM smart_user\n" + 70 | "WHERE name = 'w.dehai'\n" + 71 | "\tAND password = '123'\n" + 72 | "ORDER BY id\n" + 73 | "LIMIT 2" 74 | )); 75 | } 76 | } 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/config/HttpMsgConverterConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.config; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.module.SimpleModule; 6 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateDeserializer; 7 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateSerializer; 8 | import com.github.dreamroute.mybatis.pro.base.codec.date.LocalDateDeserializer; 9 | import com.github.dreamroute.mybatis.pro.base.codec.date.LocalDateSerializer; 10 | import com.github.dreamroute.mybatis.pro.base.codec.date.LocalDateTimeDeserializer; 11 | import com.github.dreamroute.mybatis.pro.base.codec.date.LocalDateTimeSerializer; 12 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerDeserializer; 13 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerDeserializerForCollection; 14 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerSerializerForExtra; 15 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerSerializerForExtraCollection; 16 | import org.springframework.context.annotation.Configuration; 17 | import org.springframework.http.converter.HttpMessageConverter; 18 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 19 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 20 | 21 | import java.time.LocalDate; 22 | import java.time.LocalDateTime; 23 | import java.util.Collection; 24 | import java.util.Date; 25 | import java.util.List; 26 | 27 | /** 28 | * 描述:自定义HttpMessageConverter 29 | * 30 | * @author w.dehi.2021-12-17 31 | */ 32 | @Configuration 33 | public class HttpMsgConverterConfig implements WebMvcConfigurer { 34 | 35 | @Override 36 | public void configureMessageConverters(List> converters) { 37 | 38 | // 注册自定义module 39 | SimpleModule simpleModule = new SimpleModule(); 40 | 41 | // 枚举序列化、反序列化 42 | simpleModule.addSerializer(Enum.class, new EnumMarkerSerializerForExtra()); 43 | simpleModule.addDeserializer(Enum.class, new EnumMarkerDeserializer()); 44 | simpleModule.addSerializer(Collection.class, new EnumMarkerSerializerForExtraCollection()); 45 | simpleModule.addDeserializer(Collection.class, new EnumMarkerDeserializerForCollection()); 46 | 47 | // 日期序列化、反序列化 48 | simpleModule.addSerializer(Date.class, new DateSerializer()); 49 | simpleModule.addDeserializer(Date.class, new DateDeserializer()); 50 | 51 | simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer()); 52 | simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer()); 53 | 54 | simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); 55 | simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer()); 56 | 57 | ObjectMapper mapper = new ObjectMapper(); 58 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 59 | mapper.registerModule(simpleModule); 60 | 61 | // 自定义Converter 62 | MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); 63 | converter.setObjectMapper(mapper); 64 | 65 | // 移除默认并且将自定义Converter添加到列表的第一个 66 | converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance); 67 | converters.add(0, converter); 68 | } 69 | 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/main/java/com/github/dreamroute/mybatis/pro/sample/springboot/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.mapper; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.github.dreamroute.mybatis.pro.service.mapper.BaseMapper; 5 | import lombok.Data; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.util.List; 9 | 10 | public interface UserMapper extends BaseMapper { 11 | 12 | List findByName(String name); 13 | 14 | List findByNameLimit(String name, Integer limit); 15 | 16 | User findByPassword(String password, String... cols); 17 | 18 | User findByNameAndPassword(String name, String password); 19 | 20 | List findByNameAndPasswordLike(String name, String password); 21 | 22 | List findByVersionOrderByIdDesc(Long version); 23 | 24 | List findByNameLikeAndVersionOrPasswordOrderById(String name, Long version, String password); 25 | 26 | List findByIdIn(List list); 27 | 28 | List findByIdNotIn(List list); 29 | 30 | List findByVersionLT(Long version); 31 | 32 | List findByVersionLTE(Long version); 33 | 34 | List findByVersionGT(Long version); 35 | 36 | List findByVersionGTE(Long version); 37 | 38 | List findByNameIsNull(); 39 | 40 | List findByNameIsNotNull(); 41 | 42 | List findByNameIsBlank(); 43 | 44 | List findByNameIsNotBlank(); 45 | 46 | List findByNameLike(String name); 47 | 48 | List findByNameNotLike(String name); 49 | 50 | List findByNameStartWith(String name); 51 | 52 | List findByNameEndWith(String name); 53 | 54 | List findByNameNot(String name); 55 | 56 | List findByNameIn(List list, String... cols); 57 | 58 | List findByNameNotIn(List names); 59 | 60 | List findByNameOrderByVersion(String name); 61 | 62 | List findByNameOrderByVersionDesc(String name); 63 | 64 | int countByName(String name); 65 | 66 | int countByNameAndPhoneNo(String name, String phoneNo); 67 | 68 | int countByNameAndPhoneNoOpt(String name, String phoneNo); 69 | 70 | int deleteByNameOrPassword(String name, String password); 71 | 72 | int deleteByNameOrPasswordOpt(String name, String password); 73 | 74 | boolean existByNameOrPassword(String name, String password); 75 | 76 | @Select("select * from smart_user where name = #{name} and password = #{password}") 77 | List selectByNameAndPasssword(String name, String password); 78 | 79 | List selectAllPage(User pageRequest); 80 | 81 | List findByNameAndPasswordOpt(String name, String password); 82 | 83 | List findByNameOrderByVersionDescOpt(String name); 84 | 85 | /** 86 | * 对于Opt结尾的方法,在生成sql的时候使用了这种针对字符串的风格,这里测试一下非字符串类型的查询,看是否可以兼容 87 | */ 88 | List findByVersionOpt(Long version); 89 | 90 | List findByIdLTE(long id, String... cols); 91 | 92 | FindByIdLTDto findByIdLT(Long id); 93 | 94 | List findByNameAndPasswordOrderByIdLimit(String name, String password, int limit); 95 | 96 | User findByOrderId(String orderId); 97 | 98 | @Data 99 | class FindByIdLTDto { 100 | private Long id; 101 | private String name; 102 | } 103 | 104 | List dynamicSqlSourceTest(List ids); 105 | 106 | User rawSqlSourceTest(Long id); 107 | 108 | User testWhere(String name, String password); 109 | 110 | User testTrim(String name, String password); 111 | } 112 | -------------------------------------------------------------------------------- /mybatis-pro-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.github.dreamroute 5 | mybatis-pro 6 | ${revision} 7 | 8 | mybatis-pro-boot-starter 9 | ${project.artifactId} 10 | 11 | https://github.com/Dreamroute/${project.name} 12 | 13 | ${project.url} 14 | ${project.url} 15 | ${project.url} 16 | 17 | 18 | 19 | 20 | com.github.dreamroute 21 | mybatis-pro-auto-configuration 22 | 23 | 24 | com.github.dreamroute 25 | mybatis-pro-core 26 | 27 | 28 | com.github.dreamroute 29 | mybatis-pro-service 30 | 31 | 32 | 33 | 34 | 35 | nexus 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-deploy-plugin 41 | ${maven-deploy-plugin.version} 42 | 43 | false 44 | 45 | 46 | 47 | 48 | 49 | 50 | oss 51 | 52 | 53 | 54 | org.sonatype.plugins 55 | nexus-staging-maven-plugin 56 | ${nexus-staging-maven-plugin.version} 57 | true 58 | 59 | ossrh 60 | https://oss.sonatype.org 61 | true 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-source-plugin 76 | ${maven-source-plugin.version} 77 | 78 | 79 | attach-sources 80 | 81 | jar 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-javadoc-plugin 92 | ${maven-javadoc-plugin.version} 93 | 94 | UTF-8 95 | UTF-8 96 | UTF-8 97 | 98 | 99 | 100 | attach-javadocs 101 | 102 | jar 103 | 104 | 105 | -Xdoclint:none 106 | UTF-8 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-gpg-plugin 116 | ${maven-gpg-plugin.version} 117 | 118 | 119 | sign-artifacts 120 | verify 121 | 122 | sign 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.service; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 4 | import com.ninja_squad.dbsetup.DbSetup; 5 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import javax.sql.DataSource; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | import static com.google.common.collect.Lists.newArrayList; 17 | import static com.ninja_squad.dbsetup.Operations.truncate; 18 | import static java.util.stream.Collectors.toList; 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | /** 22 | * @author w.dehai 23 | */ 24 | @SpringBootTest 25 | class UserServiceTest { 26 | 27 | @Autowired 28 | private UserService userService; 29 | @Autowired 30 | private DataSource dataSource; 31 | 32 | @BeforeEach 33 | void init() { 34 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 35 | } 36 | 37 | @Test 38 | void insertTest() { 39 | User user = User.builder().name("w.dehai").version(1L).status(1).build(); 40 | User result = userService.insert(user); 41 | assertNotNull(result.getId()); 42 | 43 | List users = userService.selectAll(); 44 | assertEquals(1, users.size()); 45 | } 46 | 47 | @Test 48 | void insertListTest() { 49 | int size = 3; 50 | List users = new ArrayList<>(3); 51 | for (int i = 0; i < size; i++) { 52 | User user = User.builder().name("w.dehai").password("123456").version(1L).phoneNo("1306006").build(); 53 | users.add(user); 54 | } 55 | userService.insertList(users); 56 | List ids = users.stream().map(User::getId).filter(Objects::nonNull).collect(toList()); 57 | assertEquals(size, ids.size()); 58 | } 59 | 60 | @Test 61 | void insertListPartitionTest() { 62 | int size = 5; 63 | List users = new ArrayList<>(22); 64 | for (int i = 0; i < size; i++) { 65 | User user = User.builder().name("w.dehai").password("123456").version(1L).phoneNo("1306006").build(); 66 | users.add(user); 67 | } 68 | userService.insertList(users, 2); 69 | List ids = users.stream().map(User::getId).filter(Objects::nonNull).collect(toList()); 70 | assertEquals(size, ids.size()); 71 | } 72 | 73 | @Test 74 | void deleteTest() { 75 | User user = User.builder().name("w.dehai").version(1L).build(); 76 | User result = userService.insert(user); 77 | assertNotNull(result.getId()); 78 | 79 | userService.delete(user.getId()); 80 | } 81 | 82 | @Test 83 | void deleteListTest() { 84 | List users = newArrayList( 85 | User.builder().name("w.dehai").version(1L).build(), 86 | User.builder().name("w.dehai").version(2L).build() 87 | ); 88 | userService.insertList(users); 89 | 90 | int result = userService.delete(newArrayList(1L, 2L)); 91 | assertEquals(2, result); 92 | } 93 | 94 | @Test 95 | void selectByIdTest() { 96 | User user = new User(); 97 | user.setId(100L); 98 | user.setName("w.dehai"); 99 | user.setPassword("123456"); 100 | user.setStatus(1); 101 | userService.insert(user); 102 | User u = userService.selectById(1L); 103 | assertEquals("w.dehai", u.getName()); 104 | assertEquals("123456", u.getPassword()); 105 | 106 | User noPwd = userService.selectById(1L, "id", "name"); 107 | assertNull(noPwd.getPassword()); 108 | } 109 | 110 | @Test 111 | void selectByIdsTest() { 112 | userService.selectByIds(newArrayList(1L, 2L)); 113 | } 114 | 115 | @Test 116 | void selectAllTest() { 117 | userService.selectAll(); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /mybatis-pro-samples/spring-boot/src/test/java/com/github/dreamroute/mybatis/pro/sample/springboot/interceptor/LimitColumnInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.sample.springboot.interceptor; 2 | 3 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.Dict; 4 | import com.github.dreamroute.mybatis.pro.sample.springboot.domain.User; 5 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.DictMapper; 6 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper; 7 | import com.github.dreamroute.mybatis.pro.sample.springboot.mapper.UserMapper.FindByIdLTDto; 8 | import com.ninja_squad.dbsetup.DbSetup; 9 | import com.ninja_squad.dbsetup.destination.DataSourceDestination; 10 | import com.ninja_squad.dbsetup.operation.Insert; 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | 16 | import javax.annotation.Resource; 17 | import javax.sql.DataSource; 18 | import java.util.List; 19 | 20 | import static com.github.dreamroute.mybatis.pro.core.util.MyBatisProUtil.FIELDS_ALIAS_CACHE; 21 | import static com.google.common.collect.Lists.newArrayList; 22 | import static com.ninja_squad.dbsetup.Operations.insertInto; 23 | import static com.ninja_squad.dbsetup.Operations.truncate; 24 | import static org.junit.jupiter.api.Assertions.assertEquals; 25 | 26 | /** 27 | * @author : w.dehai.2021.04.01 28 | */ 29 | @SpringBootTest 30 | class LimitColumnInterceptorTest { 31 | @Resource 32 | private UserMapper userMapper; 33 | @Resource 34 | private DictMapper dictMapper; 35 | @Resource 36 | private DataSource dataSource; 37 | 38 | @BeforeEach 39 | void init() { 40 | 41 | // init smat_user 42 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_user")).launch(); 43 | Insert insert = insertInto("smart_user") 44 | .columns("name", "password", "phone_no", "version", "addr_info", "status") 45 | .values("w.dehai", "123456", "1306006", 1L, "成都", 1) 46 | .values("Jaedong", "123", "1306006", 1L, "北京", 1) 47 | .values("w.dehai", "123", "1306006", 2L, "美国", 1) 48 | .build(); 49 | new DbSetup(new DataSourceDestination(dataSource), insert).launch(); 50 | 51 | // init smat_dict 52 | new DbSetup(new DataSourceDestination(dataSource), truncate("smart_dict")).launch(); 53 | Insert insert2 = insertInto("smart_dict") 54 | .columns("value", "cn_name", "status") 55 | .values(1, "有效", 1) 56 | .values(0, "无效", 1) 57 | .build(); 58 | new DbSetup(new DataSourceDestination(dataSource), insert2).launch(); 59 | } 60 | 61 | @Test 62 | void findByPasswordTest() { 63 | // 这里对同一个方法测试2次,测试缓存功能 64 | User u0 = userMapper.findByPassword("123456"); 65 | User u1 = userMapper.findByPassword("123456", "id", "name", "phoneNo"); 66 | User u2 = userMapper.findByPassword("123456", "id", "name", "password"); 67 | List u3 = userMapper.findByIdIn(newArrayList(1L)); 68 | // TODO 69 | } 70 | 71 | @Test 72 | void findByIdLTETest() { 73 | List users = userMapper.findByIdLTE(2L, "id", "name", "password", "version", "phoneNo", "addr"); 74 | assertEquals(2, users.size()); 75 | } 76 | 77 | /** 78 | * 限制列插件测试多个selectById是否会冲突,因为都存在于BaseMapper之中, 79 | */ 80 | @Test 81 | void selectByIdTest() { 82 | Dict dict = dictMapper.selectById(1L, "id", "value", "cnName", "status"); 83 | assertEquals("有效", dict.getCnName()); 84 | User user = userMapper.selectById(1L, "id", "name"); 85 | assertEquals("w.dehai", user.getName()); 86 | List all = userMapper.selectAll("id"); 87 | assertEquals(3, all.size()); 88 | List users = userMapper.selectByIds(newArrayList(1L, 2L), "id", "phoneNo"); 89 | assertEquals(2, users.size()); 90 | } 91 | 92 | @Test 93 | void cacheTest() { 94 | FindByIdLTDto byIdLT = userMapper.findByIdLT(2L); 95 | System.err.println(byIdLT); 96 | Assertions.assertTrue(FIELDS_ALIAS_CACHE.containsKey(FindByIdLTDto.class)); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/test/java/com/githu/dreamroute/mybatis/pro/base/JsonUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.githu.dreamroute.mybatis.pro.base; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.fasterxml.jackson.databind.JsonMappingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.module.SimpleModule; 7 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateDeserializer; 8 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateSerializer; 9 | import com.github.dreamroute.mybatis.pro.base.codec.enums.EnumMarkerDeserializer; 10 | import com.github.dreamroute.mybatis.pro.base.codec.enums.JsonUtil; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Date; 15 | import java.util.List; 16 | 17 | import static com.githu.dreamroute.mybatis.pro.base.Gender.FEMALE; 18 | import static com.githu.dreamroute.mybatis.pro.base.Gender.MALE; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | 22 | /** 23 | * @author w.dehai.2021/8/10.14:51 24 | */ 25 | class JsonUtilTest { 26 | 27 | private static final String BIRTHDAY_STR = "2022-05-05 15:05:12.333"; 28 | private static final Date BIRTHDAY = DateUtil.parse(BIRTHDAY_STR, DateSerializer.FORMAT); 29 | 30 | @Test 31 | void deserializeTest() throws Exception { 32 | ObjectMapper mapper = new ObjectMapper(); 33 | SimpleModule module = new SimpleModule(); 34 | module.addDeserializer(Enum.class, new EnumMarkerDeserializer()); 35 | module.addDeserializer(Date.class, new DateDeserializer()); 36 | mapper.registerModule(module); 37 | 38 | User male = mapper.readValue("{\n" + 39 | " \"id\": 100,\n" + 40 | " \"gender\": 1,\n" + 41 | " \"birthday\": \"2022-05-05 15:05:12.333\"\n" + 42 | "}", User.class); 43 | 44 | User female = mapper.readValue("{\n" + 45 | " \"id\": 100,\n" + 46 | " \"gender\": 2,\n" + 47 | " \"birthday\": \"2022-05-05 15:05:12.333\"\n" + 48 | "}", User.class); 49 | 50 | assertEquals(MALE, male.getGender()); 51 | assertEquals(FEMALE, female.getGender()); 52 | assertEquals(BIRTHDAY, male.getBirthday()); 53 | 54 | assertThrows(JsonMappingException.class, () -> mapper.readValue("{\"gender\":28,\"id\":100}", User.class)); 55 | 56 | } 57 | 58 | @Test 59 | void jsonUtilObjTest() { 60 | User user = new User(); 61 | user.setId(100L); 62 | user.setBirthday(BIRTHDAY); 63 | user.setGender(FEMALE); 64 | 65 | String str = JsonUtil.toJsonStr(user); 66 | assertEquals("{\"id\":100,\"gender\":2,\"birthday\":\"2022-05-05 15:05:12\"}", str); 67 | 68 | User u = JsonUtil.parseObj(str, User.class); 69 | assertEquals(100L, u.getId()); 70 | assertEquals(FEMALE, u.getGender()); 71 | assertEquals(BIRTHDAY, u.getBirthday()); 72 | 73 | } 74 | 75 | @Test 76 | void jsonUtilArrTest() { 77 | User user1 = new User(); 78 | user1.setId(1L); 79 | user1.setGender(MALE); 80 | 81 | User user2 = new User(); 82 | user2.setId(2L); 83 | user2.setGender(FEMALE); 84 | 85 | List users = new ArrayList<>(2); 86 | users.add(user1); 87 | users.add(user2); 88 | 89 | String str = JsonUtil.toJsonStr(users); 90 | assertEquals("[{\"id\":1,\"gender\":1,\"birthday\":null},{\"id\":2,\"gender\":2,\"birthday\":null}]", str); 91 | 92 | List us = JsonUtil.parseArr(str, User.class); 93 | assertEquals(MALE, us.get(0).getGender()); 94 | assertEquals(FEMALE, us.get(1).getGender()); 95 | } 96 | 97 | @Test 98 | void serializerForWebTest() throws Exception { 99 | 100 | User user = new User(); 101 | user.setId(100L); 102 | user.setGender(MALE); 103 | String male = JsonUtil.toJsonStrForWeb(user); 104 | assertEquals("{\"id\":100,\"gender\":{\"value\":1,\"desc\":\"男\"},\"birthday\":null}", male); 105 | 106 | user.setGender(FEMALE); 107 | String female = JsonUtil.toJsonStrForWeb(user); 108 | assertEquals("{\"id\":100,\"gender\":{\"value\":2,\"desc\":\"女\"},\"birthday\":null}", female); 109 | 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /mybatis-pro-interceptor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | mybatis-pro 8 | com.github.dreamroute 9 | ${revision} 10 | 11 | 12 | mybatis-pro-interceptor 13 | ${project.artifactId} 14 | 15 | 16 | 17 | com.github.dreamroute 18 | mybatis-pro-core 19 | 20 | 21 | 22 | 23 | 24 | nexus 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-deploy-plugin 30 | ${maven-deploy-plugin.version} 31 | 32 | false 33 | 34 | 35 | 36 | 37 | 38 | 39 | oss 40 | 41 | 42 | 43 | org.sonatype.plugins 44 | nexus-staging-maven-plugin 45 | ${nexus-staging-maven-plugin.version} 46 | true 47 | 48 | ossrh 49 | https://oss.sonatype.org 50 | true 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-source-plugin 65 | ${maven-source-plugin.version} 66 | 67 | 68 | attach-sources 69 | 70 | jar 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-javadoc-plugin 81 | ${maven-javadoc-plugin.version} 82 | 83 | UTF-8 84 | UTF-8 85 | UTF-8 86 | 87 | 88 | 89 | attach-javadocs 90 | 91 | jar 92 | 93 | 94 | -Xdoclint:none 95 | UTF-8 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-gpg-plugin 105 | ${maven-gpg-plugin.version} 106 | 107 | 108 | sign-artifacts 109 | verify 110 | 111 | sign 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /mybatis-pro-sdk/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro 7 | ${revision} 8 | 9 | mybatis-pro-sdk 10 | ${project.artifactId} 11 | 12 | 13 | 14 | com.github.dreamroute 15 | locker-spring-boot-starter 16 | ${locker-spring-boot-starter.version} 17 | 18 | 19 | 20 | 21 | 22 | nexus 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-deploy-plugin 28 | ${maven-deploy-plugin.version} 29 | 30 | false 31 | 32 | 33 | 34 | 35 | 36 | 37 | oss 38 | 39 | 40 | 41 | org.sonatype.plugins 42 | nexus-staging-maven-plugin 43 | ${nexus-staging-maven-plugin.version} 44 | true 45 | 46 | ossrh 47 | https://oss.sonatype.org 48 | true 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-source-plugin 63 | ${maven-source-plugin.version} 64 | 65 | 66 | attach-sources 67 | 68 | jar 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-javadoc-plugin 79 | ${maven-javadoc-plugin.version} 80 | 81 | UTF-8 82 | UTF-8 83 | UTF-8 84 | 85 | 86 | 87 | attach-javadocs 88 | 89 | jar 90 | 91 | 92 | -Xdoclint:none 93 | UTF-8 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-gpg-plugin 103 | ${maven-gpg-plugin.version} 104 | 105 | 106 | sign-artifacts 107 | verify 108 | 109 | sign 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /mybatis-pro-auto-configuration/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro 7 | ${revision} 8 | 9 | mybatis-pro-auto-configuration 10 | ${project.artifactId} 11 | 12 | 13 | 14 | com.github.dreamroute 15 | mybatis-pro-core 16 | 17 | 18 | com.github.dreamroute 19 | mybatis-pro-interceptor 20 | 21 | 22 | 23 | 24 | 25 | nexus 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-deploy-plugin 31 | ${maven-deploy-plugin.version} 32 | 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | oss 41 | 42 | 43 | 44 | org.sonatype.plugins 45 | nexus-staging-maven-plugin 46 | ${nexus-staging-maven-plugin.version} 47 | true 48 | 49 | ossrh 50 | https://oss.sonatype.org 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-source-plugin 66 | ${maven-source-plugin.version} 67 | 68 | 69 | attach-sources 70 | 71 | jar 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-javadoc-plugin 82 | ${maven-javadoc-plugin.version} 83 | 84 | UTF-8 85 | UTF-8 86 | UTF-8 87 | 88 | 89 | 90 | attach-javadocs 91 | 92 | jar 93 | 94 | 95 | -Xdoclint:none 96 | UTF-8 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-gpg-plugin 106 | ${maven-gpg-plugin.version} 107 | 108 | 109 | sign-artifacts 110 | verify 111 | 112 | sign 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /mybatis-pro-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.github.dreamroute 8 | mybatis-pro 9 | ${revision} 10 | 11 | mybatis-pro-service 12 | ${project.artifactId} 13 | 14 | 15 | 16 | com.github.dreamroute 17 | mybatis-pro-core 18 | 19 | 20 | javax.validation 21 | validation-api 22 | 23 | 24 | org.apache.commons 25 | commons-lang3 26 | 27 | 28 | 29 | 30 | 31 | nexus 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-deploy-plugin 37 | ${maven-deploy-plugin.version} 38 | 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | oss 47 | 48 | 49 | 50 | org.sonatype.plugins 51 | nexus-staging-maven-plugin 52 | ${nexus-staging-maven-plugin.version} 53 | true 54 | 55 | ossrh 56 | https://oss.sonatype.org 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-source-plugin 72 | ${maven-source-plugin.version} 73 | 74 | 75 | attach-sources 76 | 77 | jar 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-javadoc-plugin 88 | ${maven-javadoc-plugin.version} 89 | 90 | UTF-8 91 | UTF-8 92 | UTF-8 93 | 94 | 95 | 96 | attach-javadocs 97 | 98 | jar 99 | 100 | 101 | -Xdoclint:none 102 | UTF-8 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-gpg-plugin 112 | ${maven-gpg-plugin.version} 113 | 114 | 115 | sign-artifacts 116 | verify 117 | 118 | sign 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /mybatis-pro-base/src/main/java/com/github/dreamroute/mybatis/pro/base/codec/enums/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.base.codec.enums; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.json.JsonMapper; 7 | import com.fasterxml.jackson.databind.module.SimpleModule; 8 | import com.fasterxml.jackson.databind.type.CollectionType; 9 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 10 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateDeserializer; 11 | import com.github.dreamroute.mybatis.pro.base.codec.date.DateSerializer; 12 | 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | /** 17 | * 描述:Json工具类,枚举类型/日期类型的序列化、反序列化工具类,将EnumMaker的实现类(枚举类型)进行如下操作: 18 | *
    19 | *
  1. 枚举序列化:EnumMarker.getValue()
  2. 20 | *
  3. 枚举反序列化:EnumMarker.valueOf(value)
  4. 21 | *
  5. 日期序列化:Date -> yyyy-MM-dd HH:mm:ss.SSS
  6. 22 | *
  7. 日期反序列化:yyyy-MM-dd HH:mm:ss.SSS -> Date
  8. 23 | *
24 | * 举例: 25 | *
 26 |  *     // 性别(Gender)枚举类型
 27 |  *     public enum Gender implements EnumMarker {
 28 |  *         MALE(1, "男"),
 29 |  *         FEMALE(2, "女");
 30 |  *         private final Integer value;
 31 |  *         private final String desc;
 32 |  *     }
 33 |  *
 34 |  *     // Java实体类
 35 |  *     public class User {
 36 |  *         private String name;
 37 |  *         private Gender gender;
 38 |  *     }
 39 |  *
 40 |  * 
41 | * 42 | * @author w.dehi.2021-12-19 43 | */ 44 | public class JsonUtil { 45 | private JsonUtil() {} 46 | 47 | private static final ObjectMapper MAPPER = new JsonMapper(); 48 | private static final ObjectMapper MAPPER_FOR_WEB = new JsonMapper(); 49 | 50 | static { 51 | 52 | EnumMarkerDeserializer emd = new EnumMarkerDeserializer(); 53 | DateSerializer ds = new DateSerializer(); 54 | DateDeserializer dd = new DateDeserializer(); 55 | 56 | SimpleModule module = new SimpleModule(); 57 | module.addSerializer(Enum.class, new EnumMarkerSerializer()); 58 | module.addDeserializer(Enum.class, emd); 59 | 60 | module.addSerializer(Date.class, ds); 61 | module.addDeserializer(Date.class, dd); 62 | MAPPER.registerModule(module); 63 | MAPPER.registerModule(new JavaTimeModule()); 64 | MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 65 | 66 | SimpleModule moduleForWeb = new SimpleModule(); 67 | moduleForWeb.addSerializer(EnumMarker.class, new EnumMarkerSerializerForWeb()); 68 | moduleForWeb.addDeserializer(Enum.class, emd); 69 | 70 | moduleForWeb.addSerializer(Date.class, ds); 71 | moduleForWeb.addDeserializer(Date.class, dd); 72 | MAPPER_FOR_WEB.registerModule(moduleForWeb); 73 | MAPPER_FOR_WEB.registerModule(new JavaTimeModule()); 74 | MAPPER_FOR_WEB.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 75 | } 76 | 77 | /** 78 | * 序列化对象:将对象转换成json字符串 79 | * 80 | *
 81 |      *     {
 82 |      *         "name": "w.dehi",
 83 |      *         "gender": EnumMarkder.getValue()
 84 |      *     }
 85 |      *  
86 | * 87 | * @param target pojo对象 88 | * @return 返回json字符串 89 | */ 90 | public static String toJsonStr(Object target) { 91 | try { 92 | return MAPPER.writeValueAsString(target); 93 | } catch (JsonProcessingException e) { 94 | throw new IllegalArgumentException("序列化失败: ", e); 95 | } 96 | } 97 | 98 | /** 99 | * 序列化对象:将对象转换成json字符串 100 | *
101 |      *       {
102 |      *           "name": "w.dehi",
103 |      *           "gender": {
104 |      *               "value": EnumMarker.getValue(),
105 |      *               "desc": EnumMarker.getDesc()
106 |      *           }
107 |      *       }
108 |      *   
109 | * 110 | * @param target pojo对象 111 | * @return 返回json字符串 112 | */ 113 | public static String toJsonStrForWeb(Object target) { 114 | try { 115 | return MAPPER_FOR_WEB.writeValueAsString(target); 116 | } catch (JsonProcessingException e) { 117 | throw new IllegalArgumentException("序列化失败: ", e); 118 | } 119 | } 120 | 121 | /** 122 | * 反序列化对象:将字符串转换成pojo对象 123 | *
124 |      *     Json字符串:
125 |      *     {
126 |      *         "name": "w.deahi",
127 |      *         "gender": 1
128 |      *     }
129 |      *
130 |      * 
131 | * 132 | * @param inputJson json字符串 133 | * @param clazz pojo类型 134 | */ 135 | public static T parseObj(String inputJson, Class clazz) { 136 | try { 137 | return MAPPER.readValue(inputJson, clazz); 138 | } catch (JsonProcessingException e) { 139 | throw new IllegalArgumentException("反序列化失败, 需要被反序列化的字符串: " + inputJson, e); 140 | } 141 | } 142 | 143 | /** 144 | * 反序列化列表:将字符串转换成pojo列表 145 | * 146 | * @param inputJson json字符串 147 | * @param clazz pojo类型 148 | */ 149 | public static List parseArr(String inputJson, Class clazz) { 150 | try { 151 | CollectionType javaType = MAPPER.getTypeFactory().constructCollectionType(List.class, clazz); 152 | return MAPPER.readValue(inputJson, javaType); 153 | } catch (JsonProcessingException e) { 154 | throw new IllegalArgumentException("反序列化失败, 需要被反序列化的字符串: " + inputJson, e); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/util/DocumentUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.util; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Type; 4 | import com.github.dreamroute.mybatis.pro.core.consts.MapperLabel; 5 | import com.github.dreamroute.mybatis.pro.core.exception.MyBatisProException; 6 | import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; 7 | import org.springframework.core.io.ByteArrayResource; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.util.StringUtils; 10 | import org.w3c.dom.Attr; 11 | import org.w3c.dom.Document; 12 | import org.w3c.dom.Element; 13 | import org.w3c.dom.NamedNodeMap; 14 | import org.w3c.dom.Text; 15 | 16 | import javax.xml.XMLConstants; 17 | import javax.xml.parsers.DocumentBuilder; 18 | import javax.xml.parsers.DocumentBuilderFactory; 19 | import javax.xml.transform.OutputKeys; 20 | import javax.xml.transform.Transformer; 21 | import javax.xml.transform.TransformerFactory; 22 | import javax.xml.transform.dom.DOMSource; 23 | import javax.xml.transform.stream.StreamResult; 24 | import java.io.ByteArrayOutputStream; 25 | import java.nio.charset.StandardCharsets; 26 | 27 | import static com.github.dreamroute.mybatis.pro.core.consts.MapperLabel.MAPPER; 28 | 29 | /** 30 | * @author w.dehai 31 | */ 32 | public class DocumentUtil { 33 | 34 | private DocumentUtil() {} 35 | 36 | /** 37 | * 将Document转换成Resource 38 | */ 39 | public static Resource createResourceFromDocument(Document document) { 40 | try { 41 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); 42 | transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, document.getDoctype().getPublicId()); 43 | transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, document.getDoctype().getSystemId()); 44 | transformer.setOutputProperty("encoding", "UTF-8"); 45 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 46 | transformer.transform(new DOMSource(document), new StreamResult(bos)); 47 | 48 | String xml = bos.toString("UTF-8"); 49 | // 必须要将尖括号进行替换,否则要报错 50 | String replace = xml.replace(">", ">").replace("<", "<"); 51 | NamedNodeMap attributes = document.getElementsByTagName(MAPPER.getCode()).item(0).getAttributes(); 52 | String namespace = attributes.item(0).getTextContent(); 53 | // 这里必须要把使用带有desc参数的ByteArrayResource构造方法,因为mybatis的configuration.isResourceLoaded(resource)根据resource的toString方法判断是否存在 54 | // 而mybatis-pro插件生成的resource都是byteArrayResource,如果不带desc,那么所有的resource的toString就都相同,反之带上desc的toString方法就能区分 55 | return new ByteArrayResource(replace.getBytes(StandardCharsets.UTF_8), namespace); 56 | } catch (Exception e) { 57 | throw new MyBatisProException("Document转成Resource失败", e); 58 | } 59 | } 60 | 61 | /** 62 | * 将Resource转换成Document 63 | */ 64 | public static Document createDocumentFromResource(Resource resource) { 65 | try { 66 | DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 67 | documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); 68 | documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); 69 | documentBuilderFactory.setValidating(false); 70 | documentBuilderFactory.setNamespaceAware(false); 71 | DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); 72 | // 此处需要设置EntityResolver,以便从classpath寻找dtd文件进行解析,否则会从网路下载,很慢并且可能会connect timeout 73 | builder.setEntityResolver(new XMLMapperEntityResolver()); 74 | return builder.parse(resource.getInputStream()); 75 | } catch (Exception e) { 76 | throw new MyBatisProException("创建Document失败", e); 77 | } 78 | } 79 | 80 | /** 81 | * 给Document填充sql节点 82 | * 83 | * @param document mapper文档 84 | * @param tagName 标签 85 | * @param id id 86 | * @param resultType 返回类型 87 | * @param sql sql语句 88 | * @param type 主键是否自增 89 | */ 90 | public static void fillSqlNode(Document document, MapperLabel tagName, String id, String resultType, String sql, Type type, String idName) { 91 | Element statement = document.createElement(tagName.getCode()); 92 | 93 | Text sqlNode = document.createTextNode(sql); 94 | statement.appendChild(sqlNode); 95 | 96 | Attr idAttr = document.createAttribute(MapperLabel.ID.getCode()); 97 | idAttr.setValue(id); 98 | statement.setAttributeNode(idAttr); 99 | 100 | if (!StringUtils.isEmpty(resultType)) { 101 | Attr resultTypeAttr = document.createAttribute(MapperLabel.RESULT_TYPE.getCode()); 102 | resultTypeAttr.setValue(resultType); 103 | statement.setAttributeNode(resultTypeAttr); 104 | } 105 | 106 | if (tagName == MapperLabel.INSERT && type == Type.IDENTITY) { 107 | Attr useGeneratedKeysAttr = document.createAttribute(MapperLabel.USE_GENERATED_KEYS.getCode()); 108 | useGeneratedKeysAttr.setValue("true"); 109 | statement.setAttributeNode(useGeneratedKeysAttr); 110 | 111 | Attr keyPropertyAttr = document.createAttribute(MapperLabel.KEY_PROPERTY.getCode()); 112 | keyPropertyAttr.setValue(idName); 113 | statement.setAttributeNode(keyPropertyAttr); 114 | } 115 | 116 | document.getElementsByTagName(MapperLabel.MAPPER.getCode()).item(0).appendChild(statement); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /mybatis-pro-base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.github.dreamroute 8 | mybatis-pro 9 | ${revision} 10 | 11 | mybatis-pro-base 12 | ${project.artifactId} 13 | 14 | 15 | 16 | com.fasterxml.jackson.core 17 | jackson-databind 18 | 19 | 20 | cn.hutool 21 | hutool-all 22 | 23 | 24 | org.springframework 25 | spring-beans 26 | 27 | 28 | org.mybatis.spring.boot 29 | mybatis-spring-boot-starter 30 | 31 | 32 | org.projectlombok 33 | lombok 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | 39 | 40 | com.github.spotbugs 41 | spotbugs-annotations 42 | 4.7.2 43 | compile 44 | 45 | 46 | com.fasterxml.jackson.datatype 47 | jackson-datatype-jsr310 48 | 2.11.4 49 | 50 | 51 | 52 | 53 | 54 | nexus 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-deploy-plugin 60 | ${maven-deploy-plugin.version} 61 | 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | oss 70 | 71 | 72 | 73 | org.sonatype.plugins 74 | nexus-staging-maven-plugin 75 | ${nexus-staging-maven-plugin.version} 76 | true 77 | 78 | ossrh 79 | https://oss.sonatype.org 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-source-plugin 95 | ${maven-source-plugin.version} 96 | 97 | 98 | attach-sources 99 | 100 | jar 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-javadoc-plugin 111 | ${maven-javadoc-plugin.version} 112 | 113 | UTF-8 114 | UTF-8 115 | UTF-8 116 | 117 | 118 | 119 | attach-javadocs 120 | 121 | jar 122 | 123 | 124 | -Xdoclint:none 125 | UTF-8 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-gpg-plugin 135 | ${maven-gpg-plugin.version} 136 | 137 | 138 | sign-artifacts 139 | verify 140 | 141 | sign 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /mybatis-pro-core/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.dreamroute 6 | mybatis-pro 7 | ${revision} 8 | 9 | mybatis-pro-core 10 | ${project.artifactId} 11 | 12 | 13 | 14 | com.github.dreamroute 15 | mybatis-pro-sdk 16 | 17 | 18 | com.github.dreamroute 19 | mybatis-pro-base 20 | 21 | 22 | commons-io 23 | commons-io 24 | 25 | 26 | com.github.jsqlparser 27 | jsqlparser 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-configuration-processor 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | nexus 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-deploy-plugin 45 | ${maven-deploy-plugin.version} 46 | 47 | false 48 | 49 | 50 | 51 | 52 | 53 | 54 | oss 55 | 56 | 57 | 58 | org.sonatype.plugins 59 | nexus-staging-maven-plugin 60 | ${nexus-staging-maven-plugin.version} 61 | true 62 | 63 | ossrh 64 | https://oss.sonatype.org 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-resources-plugin 80 | ${maven-resources-plugin.version} 81 | 82 | 83 | copy-to-lombok-dir 84 | process-sources 85 | 86 | copy-resources 87 | 88 | 89 | 90 | 91 | ${project.basedir}/src/main/resources 92 | 93 | 94 | ${project.build.directory}/delombok 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-source-plugin 104 | ${maven-source-plugin.version} 105 | 106 | 107 | attach-sources 108 | 109 | jar 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-javadoc-plugin 120 | ${maven-javadoc-plugin.version} 121 | 122 | UTF-8 123 | UTF-8 124 | UTF-8 125 | 126 | 127 | 128 | attach-javadocs 129 | 130 | jar 131 | 132 | 133 | -Xdoclint:none 134 | UTF-8 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-gpg-plugin 144 | ${maven-gpg-plugin.version} 145 | 146 | 147 | sign-artifacts 148 | verify 149 | 150 | sign 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /mybatis-pro-core/src/main/java/com/github/dreamroute/mybatis/pro/core/util/ClassUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.dreamroute.mybatis.pro.core.util; 2 | 3 | import com.github.dreamroute.mybatis.pro.core.annotations.Id; 4 | import com.github.dreamroute.mybatis.pro.core.annotations.Transient; 5 | import com.github.dreamroute.mybatis.pro.core.exception.MyBatisProException; 6 | import com.github.dreamroute.mybatis.pro.sdk.Mapper; 7 | import org.apache.ibatis.annotations.Delete; 8 | import org.apache.ibatis.annotations.Insert; 9 | import org.apache.ibatis.annotations.Select; 10 | import org.apache.ibatis.annotations.Update; 11 | import org.apache.ibatis.reflection.Reflector; 12 | import org.springframework.core.ResolvableType; 13 | import org.springframework.util.ObjectUtils; 14 | 15 | import java.lang.reflect.Field; 16 | import java.lang.reflect.Method; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.HashSet; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Map.Entry; 23 | import java.util.Set; 24 | import java.util.stream.Collectors; 25 | 26 | import static cn.hutool.core.util.ReflectUtil.getFields; 27 | import static com.github.dreamroute.mybatis.pro.base.codec.enums.JsonUtil.toJsonStr; 28 | import static java.util.Arrays.stream; 29 | import static java.util.Optional.ofNullable; 30 | import static java.util.function.Function.identity; 31 | import static java.util.stream.Collectors.counting; 32 | import static java.util.stream.Collectors.groupingBy; 33 | import static java.util.stream.Collectors.toMap; 34 | import static java.util.stream.Collectors.toSet; 35 | import static org.springframework.core.annotation.AnnotatedElementUtils.hasAnnotation; 36 | import static org.springframework.util.CollectionUtils.isEmpty; 37 | 38 | /** 39 | * @author w.dehai 40 | */ 41 | public class ClassUtil { 42 | 43 | private ClassUtil() {} 44 | 45 | /** 46 | * 获取方法返回值类型,方法的返回值必须是List或者是单个类型 47 | * 48 | * @param method 方法 49 | * @return 返回值类型 50 | */ 51 | public static String getReturnType(Method method) { 52 | 53 | ResolvableType type = ResolvableType.forMethodReturnType(method); 54 | Class resolve = type.resolve(); 55 | if (resolve == List.class) { 56 | resolve = type.getGeneric(0).resolve(); 57 | } 58 | return ofNullable(resolve).orElseThrow(() -> new MyBatisProException("返回值只能是单个对象或者是List类型")).getName(); 59 | } 60 | 61 | /** 62 | * 返回Mapper接口的xxxBy开头的方法 63 | * 64 | * @param interfaceCls Mapper接口 65 | * @return findBy开头的方法的方法名字 66 | */ 67 | public static List getSpecialMethods(Class interfaceCls) { 68 | Method[] methods = interfaceCls.getDeclaredMethods(); 69 | List names = new ArrayList<>(); 70 | for (Method m : methods) { 71 | String name = m.getName(); 72 | boolean isBy = name.startsWith("findBy") || name.startsWith("deleteBy") || name.startsWith("countBy") || name.startsWith("existBy"); 73 | boolean isAnnoCrud = hasAnnotation(m, Select.class) || hasAnnotation(m, Update.class) || hasAnnotation(m, Insert.class) || hasAnnotation(m, Delete.class); 74 | if (isBy && isAnnoCrud) { 75 | throw new MyBatisProException("接口方法" + interfaceCls.getName() + "." + name + 76 | "是[findBy, deleteBy, countBy, existBy]之一, 会被MybatisPro框架自动创建SQL语句,不能使用[@Select, @Update, @Insert, @Delete]来自定义SQL,请对方法重新命名"); 77 | } 78 | if (isBy) { 79 | names.add(name); 80 | } 81 | } 82 | return names; 83 | } 84 | 85 | 86 | 87 | /** 88 | * 获取Mapper的所有方法名 89 | */ 90 | public static Set getBaseMethodNames() { 91 | Set> parentInterfaces = getAllParentInterface(Mapper.class); 92 | return parentInterfaces.stream() 93 | .map(Class::getDeclaredMethods) 94 | .flatMap(Arrays::stream) 95 | .map(Method::getName) 96 | .collect(toSet()); 97 | } 98 | 99 | /** 100 | * 获取Mapper方法名与返回值对应的map, Map 101 | * 102 | * @param interfaceCls 接口 103 | * @return 返回映射关系 104 | */ 105 | public static Map getMethodName2ReturnType(Class interfaceCls) { 106 | Method[] ms = interfaceCls.getMethods(); 107 | Map methodCount = stream(ms).map(Method::getName).collect(groupingBy(identity(), counting())); 108 | Map duplicateMethods = methodCount.entrySet().stream().filter(count -> count.getValue() != 1L).collect(toMap(Entry::getKey, Entry::getValue)); 109 | if (!isEmpty(duplicateMethods)) { 110 | throw new MyBatisProException(interfaceCls.getName() + "的方法: " + toJsonStr(duplicateMethods.keySet()) + "存在重复的方法名,MyBatis-Pro不允许Mapper存在同名方法"); 111 | } 112 | return stream(ms).filter(m -> { 113 | String name = m.getName(); 114 | return name.startsWith("findBy") || name.startsWith("countBy") || name.startsWith("existBy") || name.startsWith("deleteBy"); 115 | }).collect(toMap(Method::getName, ClassUtil::getReturnType)); 116 | } 117 | 118 | /** 119 | * 获取接口的所有父接口 120 | */ 121 | public static Set> getAllParentInterface(Class cls) { 122 | Set> result = new HashSet<>(); 123 | recursiveCls(cls, result); 124 | return result; 125 | } 126 | 127 | private static void recursiveCls(Class cls, Set> result) { 128 | Class[] interfaces = cls.getInterfaces(); 129 | if (!ObjectUtils.isEmpty(interfaces)) { 130 | result.addAll(Arrays.asList(interfaces)); 131 | Arrays.stream(interfaces).forEach(inter -> recursiveCls(inter, result)); 132 | } 133 | } 134 | 135 | /** 136 | * 获取实体所有JavaBean属性: 137 | *
    138 | *
  1. JavaBean属性
  2. 139 | *
  3. 未被@Transient标记
  4. 140 | *
141 | */ 142 | public static Set getAllFields(Class cls) { 143 | Field[] fs = getFields(cls); 144 | Reflector r = new Reflector(cls); 145 | return Arrays.stream(fs) 146 | .filter(f -> isJavaBeanProp(r, f)) 147 | .collect(Collectors.toSet()); 148 | } 149 | 150 | public static boolean isJavaBeanProp(Reflector r, Field field) { 151 | return r.hasGetter(field.getName()) && r.hasSetter(field.getName()) && !hasAnnotation(field, Transient.class); 152 | } 153 | 154 | public static Field getIdField(Class cls) { 155 | Set idFields = getAllFields(cls).stream().filter(field -> field.isAnnotationPresent(Id.class)).collect(Collectors.toSet()); 156 | if (idFields.size() != 1) { 157 | throw new MyBatisProException("实体" + cls.getName() + "必须要有且仅有一个被@Id注解标注的主键字段"); 158 | } 159 | return idFields.iterator().next(); 160 | } 161 | } 162 | 163 | --------------------------------------------------------------------------------