├── .gitignore ├── README.md ├── docs ├── 关于CRUD功能.md ├── 关于国际化功能.md ├── 关于实体属性检查功能.md ├── 关于高级搜索功能.md └── 版本历史.md ├── pom.xml └── src ├── main ├── java │ └── vip │ │ └── efactory │ │ └── ejpa │ │ └── example │ │ ├── EjpaExampleApplication.java │ │ ├── config │ │ ├── CacheObserveBeanPostProcessor.java │ │ ├── DataSourceBeanPostProcessor.java │ │ ├── MultiTenantJpaConfiguration.java │ │ └── RedisTemplateConfiguration.java │ │ ├── controller │ │ ├── CourseController.java │ │ ├── StudentController.java │ │ ├── SysTenantController.java │ │ └── TeacherController.java │ │ ├── entity │ │ ├── Course.java │ │ ├── Student.java │ │ ├── SysTenant.java │ │ └── Teacher.java │ │ ├── repository │ │ ├── CourseRepository.java │ │ ├── StudentRepository.java │ │ ├── SysTenantRepository.java │ │ └── TeacherRepository.java │ │ └── service │ │ ├── ICourseService.java │ │ ├── IStudentService.java │ │ ├── ISysTenantService.java │ │ ├── ITeacherService.java │ │ └── impl │ │ ├── CourseServiceImpl.java │ │ ├── StudentServiceImpl.java │ │ ├── SysTenantServiceImpl.java │ │ └── TeacherServiceImpl.java └── resources │ ├── application.yml │ ├── i18n │ ├── messages.properties │ ├── messages_de_DE.properties │ ├── messages_en_CA.properties │ ├── messages_en_GB.properties │ ├── messages_en_US.properties │ ├── messages_fr_CA.properties │ ├── messages_fr_FR.properties │ ├── messages_it_IT.properties │ ├── messages_ja_JP.properties │ ├── messages_ko_KR.properties │ ├── messages_ru_KZ.properties │ ├── messages_ru_RU.properties │ ├── messages_zh_CN.properties │ └── messages_zh_TW.properties │ ├── sql │ ├── MutiTenantTest.sql │ └── student_test_data.sql │ └── static │ ├── payimg │ ├── ali-pay.png │ └── wx-pay.png │ └── usage │ ├── advQuery │ ├── 单条件查询 │ │ ├── 1-ISNULL-Query.png │ │ ├── 1-NOTNULL-Query.png │ │ ├── 2-Fuzzy-Query.png │ │ ├── 2-LeftFuzzy-Query.png │ │ ├── 2-RightFuzzy-Query.png │ │ ├── 3-EqualQuery.png │ │ ├── 3-NotEqualQuery.png │ │ ├── 4-BetweenQuery.png │ │ ├── 5-GE-Query.png │ │ ├── 5-GT-Query.png │ │ ├── 5-LE-Query.png │ │ └── 5-LT-Query.png │ ├── 多条件不分组查询 │ │ └── 1-多条件AND查询.png │ ├── 多条件分组查询 │ │ └── 1-多条件分组查询.png │ └── 数据记录.png │ ├── check │ └── 实体属性约束检查.png │ ├── crud │ ├── AdvanceQuery.png │ ├── DeleteByID.png │ ├── GetById.png │ ├── Page.png │ ├── Save.png │ ├── Update.png │ └── fuzzy.png │ └── i18n │ ├── 1-在头中带英文的国际化参数.png │ ├── 3-在请求地址中带英文的国际化参数.png │ ├── 0-不带国际化参数,默认中文简体.png │ ├── 2-在头中带中文的国际化参数.png │ └── 4-在请求地址中带中文的国际化参数.png └── test └── java └── vip └── efactory └── ejpa └── example └── EjpaExampleApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 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 | 30 | ### VS Code ### 31 | .vscode/ 32 | /ejpa-example.iml 33 | /HELP.md 34 | /mvnw 35 | /mvnw.cmd 36 | /.mvn/wrapper/ 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ejpa-example 2 | ejpa框架使用的案例项目 3 | 4 | # 初衷 5 | - 在项目开发中,经常发现大多数的情况下都是一些频次非常高的,相似性非常大的操作,如果为了便捷复制粘贴代码,是可以快速完成任务,但也会为后来的维护带来痛苦的体验。 6 | - 故,本项目的目的就是充分思考抽取项目中具有[频次非常高的,相似性非常大的操作]来实现,来方便维护,同时提高开发效率。 7 | 8 | # ejpa框架原理 9 | - 其实就是基于SpringDataJpa的再封装,同时提供一些常用操作的模板以便使用。 10 | - 从Controller-->Service-->Repository-->Entity,都有相应的模板代码. 11 | 12 | # 框架特性(v3.5.0) 13 | ## 多租户模式 14 | ### 基于独立数据库的多租户 15 | - 接口测试效果说明: 16 | 17 | - 使用过程: 18 | 19 | ### 基于数据列的多租户 20 | - Hibernate还没有实现这种方式; 21 | - 可以自己考虑用复合主键的方式来实现。 22 | 23 | ### 基于Scheme的多租户 24 | - 在MySQL数据库中和基于数据库模式似乎没有什么不同。 25 | 26 | # 框架特性(v3.0.0) 27 | - 基本的CRUD模板,即增删改查操作,此处的查是指正常的分页、排序及id查询; 28 | - 较复杂的多条件的高级查询; 29 | - 提供对持久化实体属性操作检查功能; 30 | - 提供接口的国际化功能; 31 | - 自动跟踪记录的:创建时间、更新时间、创建人、更新人 32 | - 更具体文档说明见:/docs下文件 33 | 34 | # V4.0.0 计划 35 | - 加持redis的缓存配置; 36 | - 对请求加持日志记录; 37 | - 对底层的数据库异常增加处理; 38 | - 对敏感信息脱敏处理 39 | 40 | ## 详细版本说明参见 41 | - 42 | 43 | # 相关项目说明 44 | - 国际化实现:https://github.com/vip-efactory/common-i18n 45 | - 通用国际化功能实现,被ejpa框架依赖。 46 | - ejpa框架项目:https://github.com/vip-efactory/ejpa 47 | - ejpa的主体实现 48 | - ejpa框架Starter:https://github.com/vip-efactory/ejpa-spring-boot-starter 49 | - 封装了ejpa需要的所有依赖 50 | - ejpa案例项目:https://github.com/vip-efactory/ejpa-example 51 | - ejpa的使用案例,以及功能是否正常的测试项目 52 | 53 | # 如何使用 54 | - 1.引入已发布的starter依赖: 55 | 56 | ``` 57 | 58 | 1.8 59 | 3.0.0 60 | 8.0.18 61 | 62 | 63 | vip.efactory 64 | ejpa-spring-boot-starter 65 | ${ejpa.version} 66 | pom 67 | 68 | 69 | mysql 70 | mysql-connector-java 71 | runtime 72 | ${mysql.driver.version} 73 | 74 | ``` 75 | 76 | - 2.编写application主文件 77 | ``` 78 | ackage vip.efactory.ejpa.example; 79 | 80 | import org.springframework.boot.SpringApplication; 81 | import org.springframework.boot.autoconfigure.SpringBootApplication; 82 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 83 | 84 | @EnableJpaAuditing // 创建时间、创建人等字段的处理 85 | @SpringBootApplication 86 | public class EjpaExampleApplication { 87 | 88 | public static void main(String[] args) { 89 | SpringApplication.run(EjpaExampleApplication.class, args); 90 | } 91 | 92 | } 93 | ``` 94 | 95 | - 3.用IDEA运行项目即可 96 | 97 | - 更详细使用方式,请参考案例项目:https://github.com/vip-efactory/ejpa-example 98 | 99 | # 测试案例图片 100 | - 测试案例参见:resources/static/usege 101 | 102 | # 如何贡献代码 103 | - 克隆代码 104 | - git clone https://github.com/vip-efactory/common-i18n.git 105 | - git clone https://github.com/vip-efactory/ejpa.git 106 | - git clone https://github.com/vip-efactory/ejpa-example.git 107 | - Maven编译出本地安装包 108 | - 在common-i18n项目根目录执行:mvn clean install 109 | - 在ejpa项目根目录执行:mvn clean install 110 | - 在ejpa-example项目根目录执行:mvn clean install 111 | - 运行样例项目 112 | - 在本地的MySQL数据库中创建:db_ejpa_example,相关的表会在项目运行时自动创建 113 | - 编译后直接在IDEA中运行ejpa-example项目即可; 114 | - 若更改common-i18n或ejpa项目的源码,需要删除本地maven仓库的jar,否则冲突。 115 | - 清除方式: 116 | - A.找到本机maven仓库目录,例如我的是:/media/dbdu/BK/m2/repository; 117 | - B.删除vip目录下的所有文件; 118 | - C.重新Maven编译出本地安装包即可 119 | - 修改源码增加新特性 120 | - 在github上提交pull请求 121 | - 122 | 123 | # 相关说明: 124 | - 关于校验文件的占位符 125 | - 实现校验国际化信息里的占位符处理:占位符支持注解的属性占位例如{min}-{max},不支持{0}-{1}这样的占位,因为校验注解不支持传参,只能使用已有的属性信息当参数。 126 | - 关于案例项目中创建人和更新人为空的问题,是因为没有加持登录功能,所以系统抓不到这两个字段的信息。 127 | 128 | 129 | # 已知问题 130 | 131 | # 注意 132 | - 请不要直接使用ejpa项目的master分支代码,在开发中可能非常不稳定,可以使用已发布的分支! 133 | 134 | # 捐赠支持 135 | - 如果项目对您有帮助,就支持一下吧,你可以: 136 | - 对项目进行star 137 | - 赏杯雪碧吧 138 | - 支付宝、微信: 139 | 140 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/payimg/ali-pay.png) 141 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/payimg/wx-pay.png) 142 | 143 | # 技术交流群: 144 | - QQ群:1013796294 145 | - 微信群: 146 | -------------------------------------------------------------------------------- /docs/关于CRUD功能.md: -------------------------------------------------------------------------------- 1 | # CRUD 2 | - 本框架提供了基本的增删该查的功能: 3 | - 主要实现在ejpa:vip.efactory.ejpa.base包里 4 | 5 | # 分页及排序 6 | public R getByPage(Pageable page) {...},使用jpa默认的分页对象,后面可能考虑不使用这个,这个对象太繁杂了。 7 | 8 | # 通过主键获取记录 9 | public R getById(ID id) {...} 10 | 11 | # 保存记录 12 | public R save(T1 entity) {...} ,如果实体上有注解校验约束,则会被检查! 13 | 14 | # 更新记录 15 | public R updateById(T1 entity) {...},如果实体上有注解校验约束,则会被检查! 16 | 17 | # 通过主键删除记录 18 | public R deleteById(ID id) {...} 19 | 20 | # 多字段模糊查询含有的内容 21 | 多字段模糊查询,例如:q=abc&fields=name,address,desc,意思是查询在name,address,desc三个字段中包含abc的记录。 22 | http://localhost:8080/student/fuzzy?fields=name,version&q=BB 23 | - 不分页 24 | 25 | `` 26 | public R queryMutiField(String q, String fields) {...} 27 | `` 28 | - 分页 29 | 30 | `` 31 | public R queryMutiField(String q, String fields, Pageable page) {...} 32 | `` 33 | 34 | 更多具体的实现,参见:vip.efactory.ejpa.base.controller.BaseController类 35 | 36 | public class StudentController extends BaseController {...} 37 | BaseController指定了3个泛型:分别是:实体类型,实体的service层及主键类型。 38 | 39 | 注意:因为继承BaseController 会默认已经注入了Service层,如果不想使用父类的实现,可以使用entityService对象调用服务层的方法! -------------------------------------------------------------------------------- /docs/关于国际化功能.md: -------------------------------------------------------------------------------- 1 | # 实现主题 2 | - common-i18n为ejpa国际化功能的主体实现; 3 | 4 | 5 | # 3方面实现国际化 6 | - 错误码 7 | - 实体属性校验 8 | - 实体字段名称 9 | 10 | # 默认加载国际化资源的路径 11 | - "classpath:i18n/CommErrorCode" 12 | - "classpath:i18n/ValidationMessages" 13 | - "classpath:i18n/messages" 14 | 15 | # 国际化主要配置类 16 | - vip.efactory.common.i18n.config.LocaleConfig 17 | 18 | # 默认国际化区域 19 | - 默认国际化区域为:zh_CN, 20 | - 目前支持切换到:en_US,若想支持其他的区域国际化,需要提供相关的国际化资源文件; 21 | - 请求接口默认国际化环境为:中文简体,即:zh_CN,有两种方式变更区域: 22 | - 在请求头Header中,携带locale参数及正确值,就是使用头中指定的国际化区域; 23 | ``` 24 | POST http://localhost:8080/student 25 | Request Headers 26 | Content-Type: application/json 27 | locale: en_US 28 | ... 29 | ``` 30 | - 在请求接口url上有参数locale及正确值。 31 | ``` 32 | http://localhost:8080/student?locale=en_US 33 | ``` 34 | 35 | # 使用案例 36 | - 不带locale参数,默认为zh_CN: 37 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/i18n/%EF%BC%90-%E4%B8%8D%E5%B8%A6%E5%9B%BD%E9%99%85%E5%8C%96%E5%8F%82%E6%95%B0%EF%BC%8C%E9%BB%98%E8%AE%A4%E4%B8%AD%E6%96%87%E7%AE%80%E4%BD%93.png) 38 | - 使用请求头设置的locale参数 39 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/i18n/1-%E5%9C%A8%E5%A4%B4%E4%B8%AD%E5%B8%A6%E8%8B%B1%E6%96%87%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96%E5%8F%82%E6%95%B0.png) 40 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/i18n/%EF%BC%92-%E5%9C%A8%E5%A4%B4%E4%B8%AD%E5%B8%A6%E4%B8%AD%E6%96%87%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96%E5%8F%82%E6%95%B0.png) 41 | 42 | - 使用请求URL带参数locale参数 43 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/i18n/3-%E5%9C%A8%E8%AF%B7%E6%B1%82%E5%9C%B0%E5%9D%80%E4%B8%AD%E5%B8%A6%E8%8B%B1%E6%96%87%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96%E5%8F%82%E6%95%B0.png) 44 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/i18n/%EF%BC%94-%E5%9C%A8%E8%AF%B7%E6%B1%82%E5%9C%B0%E5%9D%80%E4%B8%AD%E5%B8%A6%E4%B8%AD%E6%96%87%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96%E5%8F%82%E6%95%B0.png) 45 | 46 | 案例图中英文环境,仍然有中文,那是因为是硬编码的信息,所以不会国际化变更而改变,此处为了测试故意为之! 47 | ``` 48 | @PositiveOrZero(message = "{student.age} {property.not.allow.negative}") 49 | private Integer age; 50 | 51 | /** 52 | * 爱好,测试不使用国际化文件,直接硬编码返回! 53 | */ 54 | @NotBlank(message = "爱好属性信息不允许为空") 55 | private String hobby; 56 | ``` 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/关于实体属性检查功能.md: -------------------------------------------------------------------------------- 1 | # 实体属性检查 2 | - 将检查注解加持到实体属性上,例如:vip.efactory.ejpa.example.entity.Student 3 | - 在请求到来时使用@Valid注解校验或者用工具类手动校验 4 | - 本框架中使用工具类手动校验,返回自己需要的格式。 5 | 6 | # 属性检查核心代码 7 | - 保存和更新时会自动校验属性约束:vip.efactory.ejpa.base.controller.BaseController 8 | ``` 9 | /** 10 | * Description:保存一个实体,保存之前会做检查 11 | * 12 | * @param entity 要保存的实体对象 13 | * @return R 14 | */ 15 | public R save(T1 entity) { 16 | // 实体校验支持传递组规则,不传递则为Default组! 17 | Map errors = ValidateModelUtil.validateModel(entity); 18 | 19 | if (!errors.isEmpty()) { 20 | return R.error(CommAPIEnum.PROPERTY_CHECK_FAILED).setData(errors); 21 | } 22 | ...省略剩余部分代码 23 | } 24 | 25 | /** 26 | * Description:使用id来更新,如果属性空值,则不更新现有的值 27 | * 28 | * @param entity 要更新的实体对象 29 | * @return R 30 | */ 31 | public R updateById(T1 entity) { 32 | // 检查实体的属性是否符合校验规则,使用Update组来校验实体, 33 | Map errors = ValidateModelUtil.validateModel(entity, Update.class); // 可以传递多个校验组! 34 | 35 | if (!errors.isEmpty()) { 36 | return R.error(CommAPIEnum.PROPERTY_CHECK_FAILED).setData(errors); 37 | } 38 | ...省略剩余部分代码 39 | } 40 | ``` 41 | 42 | # 属性检查的工具类核心实现 43 | ``` 44 | @Component 45 | @Slf4j 46 | public class ValidateModelUtil { 47 | private static ILocaleMsgSourceService localeMessageSourceService; 48 | 49 | //通过下面的方法为静态成员赋值!!! 50 | @Autowired 51 | public void setLocaleMessageSourceService(ILocaleMsgSourceService localeMessageSourceService) { 52 | ValidateModelUtil.localeMessageSourceService = localeMessageSourceService; 53 | } 54 | 55 | //验证某一个对象,可以指定激活哪个校验组,例如Update.class 56 | public static Map validateModel(Object obj,Class... groups) { 57 | 58 | //用于存储验证后的错误信息 59 | Map errors = new TreeMap<>(); 60 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); 61 | 62 | //验证某个对象,其实也可以只验证其中的某一个属性的 63 | Set> constraintViolations = validator.validate(obj, groups); 64 | Iterator> iter = constraintViolations.iterator(); 65 | 66 | while (iter.hasNext()) { 67 | ConstraintViolation currentObj = iter.next(); //再执行一次就会跳到下一个对象 68 | String message = currentObj.getMessage(); //校验要显示的错误信息 69 | String property = currentObj.getPropertyPath().toString(); //错误信息对应的字段名 70 | 71 | // log.info("property:" + property + ",check failed:" + message); 72 | 73 | //处理属性切分,例如:message="AAA{student.age}BBB {property.not.allow.negative}CCCC" 74 | if (message.contains("{") && message.contains("}")) { //说明包含占位符{} 75 | String[] rawKeys = message.split("}");//得到:AAA{student.age、BBB {property.not.allow.negative、CCCC 76 | // 得到所有的key 77 | List keys = new ArrayList<>(); 78 | for (String rawKey : rawKeys) { 79 | if (rawKey.contains("{")) { 80 | String key = rawKey.substring(rawKey.lastIndexOf("{") + 1); //获取key,例如:student.age 81 | keys.add(key); 82 | } 83 | } 84 | // 替换message里的占位符 85 | for (String key : keys) { 86 | String value = localeMessageSourceService.getMessage(key); 87 | if (null == value || value.equals(key)) { 88 | value = key; 89 | log.warn("missing key [{}]", key); 90 | } 91 | message = message.replace("{" + key + "}", value); 92 | } 93 | 94 | // 替换message 里国际化信息里的占位符,使用注解自带的属性值占位,例如:{min}-{max} 95 | if (message.contains("{") && message.contains("}")) { 96 | // 得到检查注解里的参数,以便再次替换模板里的占位符 97 | Map params = currentObj.getConstraintDescriptor().getAttributes(); 98 | for (Map.Entry param : params.entrySet()) { 99 | message = message.replace("{" + param.getKey() + "}", param.getValue().toString()); 100 | } 101 | } 102 | 103 | errors.put(property, message); 104 | } else { 105 | //是硬编码的校验信息,直接取过来 106 | errors.put(property, message); 107 | } 108 | 109 | } 110 | return errors; 111 | } 112 | } 113 | ``` 114 | 115 | # 使用实例 116 | - 实例代码: 117 | ``` 118 | /** 119 | * 学生表,用来测试接口是否正常 120 | * 对于校验注解,没有指定groups属性的,则默认是Default.class组, 121 | * 控制器里在save方法是使用默认组, 122 | * updateById,使用Update.class组,更新时允许部分字段为空,但是若有值则校验属性值的合法性 123 | */ 124 | @Setter 125 | @Getter 126 | @Entity 127 | public class Student extends BaseEntity { 128 | 129 | @Id 130 | @GeneratedValue(strategy = GenerationType.AUTO) 131 | @NotNull(message = "id {property.not.allow.empty}", groups = Update.class) // 意味着,updateById更新时id不允许为空 132 | private Long id; 133 | 134 | // 不论新增和更新都应符合要求 135 | @Length(min = 4, max = 8, message = "{student.name} {property.length.in.between}", groups = {Update.class, Default.class}) 136 | // 意思是,新增时不允许为空,updateById更新时可为空! 137 | @NotBlank(message = "{student.name} {property.not.allow.empty}") 138 | private String name; 139 | 140 | @Range(min = 0, max = 2, message = "{student.sex} {property.value.in.range}") 141 | private Integer sex; // 0 未知;1 男;2 女 142 | 143 | @PositiveOrZero(message = "{student.age} {property.not.allow.negative}") 144 | private Integer age; 145 | 146 | /** 147 | * 爱好,测试不使用国际化文件,直接硬编码返回! 148 | */ 149 | @NotBlank(message = "爱好属性信息不允许为空") 150 | private String hobby; 151 | 152 | /** 153 | * 身高,单位cm,例如:180.6cm 154 | */ 155 | @Max(value = 300, message = "{student.height}{property.value.should.lt.max}") 156 | private Double height; 157 | 158 | @Email(message = "{student.email}{property.format.error}") 159 | private String email; 160 | 161 | @Past(message = "{student.birthday}{property.value.only.past}") 162 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 163 | @Temporal(TemporalType.DATE) 164 | private Date birthday; 165 | 166 | } 167 | ``` 168 | - 注解message属性解析 169 | ``` 170 | /** 171 | * 爱好,测试不使用国际化文件,直接硬编码返回!不会因为国际化环境变化而变化,非常不建议这样写! 172 | */ 173 | @NotBlank(message = "爱好属性信息不允许为空") 174 | private String hobby; 175 | 176 | /** 177 | * name 字段有非空检查和长度检查,长度范围是4-8位 178 | * {student.name}和{property.length.in.between}将会取国际化资源文件里的key对应的值! 179 | */ 180 | @Length(min = 4, max = 8, message = "{student.name} {property.length.in.between}") 181 | @NotBlank(message = "{student.name} {property.not.allow.empty}") 182 | private String name; 183 | ``` 184 | - 注解groups属性解析 185 | ``` 186 | 没有指定groups属性的,则默认是Default.class组, 187 | 控制器(BaseController)里在save方法是使用默认组, 188 | updateById,使用Update.class组,更新时允许部分字段为空,但是若有值则校验属性值的合法性 189 | ``` 190 | 191 | 在messages_zh_CN.properties文件中: 192 | ``` 193 | student.age=年龄 194 | student.birthday=生日 195 | student.email=邮件地址 196 | student.height=身高 197 | student.hobby=爱好 198 | student.name=姓名 199 | student.sex=性别 200 | ``` 201 | 而{property.not.allow.empty}在common-i18n项目ValidationMessages_zh_CN.properties中: 202 | ``` 203 | property.not.allow.empty=属性不允许为空! 204 | property.format.error=属性格式不正确! 205 | property.length.in.between=属性长度应在{min}-{max}之间 206 | property.value.should.gt.min=属性值不能小于{value} 207 | property.value.should.lt.max=属性值不能大于{value} 208 | property.not.allow.negative=属性值不允许为负数 209 | property.not.allow.positive=属性值不允许为正数 210 | property.value.only.past=属性值只允许为过去的日期时间 211 | property.value.only.future=属性值只允许为将来的日期时间 212 | property.value.in.range=属性值应在{min}-{max}范围之间 213 | ``` 214 | 215 | # 注意 216 | - 对于校验文件的国际化文件里的占位符变量不能随便写,必须是校验注解自己的属性,因为现在校验注解不支持自己传递占位符参数。 217 | 218 | 219 | # 额外信息 220 | @Valid注解和@Validated注解的区别:https://www.cnblogs.com/myinspire/articles/7649027.html 221 | - @Valid是使用hibernate validation的时候使用 222 | - @Validated 是只用spring Validator 校验机制使用 223 | 224 | - 网上其他网友使用案例: 225 | ``` 226 | 使用@Validated 注解的方式: 227 | @Log("新增部门") 228 | @ApiOperation("新增部门") 229 | @PostMapping 230 | @PreAuthorize("@el.check('dept:add')") 231 | public ResponseEntity create(@Validated @RequestBody Dept resources){ 232 | if (resources.getId() != null) { 233 | throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); 234 | } 235 | return new ResponseEntity<>(deptService.create(resources),HttpStatus.CREATED); 236 | } 237 | 238 | @Log("修改部门") 239 | @ApiOperation("修改部门") 240 | @PutMapping 241 | @PreAuthorize("@el.check('dept:edit')") 242 | public ResponseEntity update(@Validated(Dept.Update.class) @RequestBody Dept resources){ 243 | deptService.update(resources); 244 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 245 | } 246 | 247 | 248 | @RequestMapping(value = "/page/add", method = RequestMethod.POST) 249 | @ResponseBody 250 | public ResultData addUrlRule(@Validated(AppConfigGroup.Add.class) AppConfigList appConfigList, BindingResult result, Model model) { 251 | // 后台校验 252 | if (ValidateHandler.validate(result, model)) { 253 | return null; 254 | } 255 | appConfigList.setStatus((byte) 1); 256 | appConfigList.setCreateTime(new Date()); 257 | appConfigList.setModifTime(new Date()); 258 | Double orders = appConfigService.selectOrdersByAppId(appConfigList.getAppId()); 259 | if (orders == null) { 260 | appConfigList.setOrders((double) 1000); 261 | } else { 262 | appConfigList.setOrders(orders + 1000); 263 | } 264 | return getResult(appConfigService.insert(appConfigList)); 265 | } 266 | ``` 267 | 268 | 使用:@Valid注解的方式,具体参见:https://www.cnblogs.com/happyflyingpig/p/8022881.html 269 | ``` 270 | @RequestMapping("/add") 271 | public String addUser(@Valid @ModelAttribute("userModel") UserModel userModel, BindingResult bindingResult, Model model) { 272 | if (bindingResult.hasErrors()) { 273 | FieldError fieldError = bindingResult.getFieldError(); 274 | String validMess = fieldError.getDefaultMessage(); 275 | model.addAttribute("errors", validMess); 276 | return "error"; 277 | } 278 | ... 279 | //用重定向防止页面刷新重复提交 280 | return "redirect: /user/index"; 281 | } 282 | ``` -------------------------------------------------------------------------------- /docs/关于高级搜索功能.md: -------------------------------------------------------------------------------- 1 | # 请求的方法类型: 2 | - 因为高级查询需要携带很多的参数,故所有的请求都是POST方法! 3 | 4 | # 单个条件高级搜索: 5 | - 支持13种常见的操作,具体参见:vip.efactory.ejpa.base.enums.SearchTypeEnum 6 | ``` 7 | @Getter 8 | public enum SearchTypeEnum { 9 | FUZZY(0, "模糊查询"), 10 | EQ(1, "等于查询"), 11 | RANGE(2, "范围查询"), 12 | NE(3, "不等于查询"), 13 | LT(4, "小于查询"), 14 | LE(5, "小于等于查询"), 15 | GT(6, "大于查询"), 16 | GE(7, "大于等于查询"), 17 | IS_NULL(8, "Null值查询"), // 3.0+ 18 | NOT_NULL(9, "非Null值查询"), // 3.0+ 19 | LEFT_LIKE(10, "左模糊查询"), // 3.0+ 20 | RIGHT_LIKE(11, "右模糊查询"), // 3.0+ 21 | IN(12, "包含查询"), // 3.4+ ,内置暂不支持not in查询!除非手写hql或sql实现 22 | ; 23 | ... 24 | } 25 | ``` 26 | 27 | # 多个条件: 28 | - 所有默认或关系:A=4 || B=7 || C > 8 // 1.0+ 29 | - 所有条件与关系:A=4 && B=7 && C > 8 // 1.0+ 30 | 31 | - 与或混合关系: // 3.0+ 32 | - A=4 && B=7 || C > 8 33 | - 半混合关系:(含有括号组) // 3.0+ 34 | - A=4 && B =7 && (C =9 || C =11) 35 | - A=4 || B =7 || (C =9 && D =11) 36 | - 全混合关系:(含有括号组) // 3.0+ 37 | - (A = 4 || B = 7) && (C =9 || C =11 ) 38 | - (A = 4 && B = 7) || (C =9 && D =11 ) 39 | 40 | # 关于条件定义说明 41 | - 具体参见:vip.efactory.ejpa.base.entity.BaseSearchField,说明说明如下: 42 | ``` 43 | name: 搜索字段名,例如:是name字段或者age字段查询,非空必填 44 | 45 | searchType:搜索类型,例如:0--模糊查询;1--等于查询;2--范围查询;3--不等于查询;4--小于查询;5--小于等于查询;6--大于查询;7--大于等于查询... 非空必填 46 | 47 | val:字段值或开始值,范围查询时为开始值,NULL或NOT NULL查询时可为空; 48 | 49 | val2:结束值,仅在范围查询时需要;其他情况可为空 50 | 51 | order:条件位置顺序,条件在前面还是在后面,有则使用!可为空。 52 | 53 | logicalType:条件逻辑类型,当前条件与其他条件之间的或与关系。 54 | 55 | bracketsGroup:括号组名,哪些条件在同一个组里,例如:(a=3 || b=4) && (c=5 || d=7),a条件和b条件就属于同一个组,应具有相同的组名,组名除默认外可任意命名。 56 | 57 | logicalTypeGroup:组逻辑类型,组逻辑类型,当前组条件是与的关系还是或的关系,0为或的关系,1为与的关系,若为空,默认为0,即: 58 | 是||(a=3 || b=4) 59 | 还是 &&(c=5 || d=7)的关系 60 | 注意,同一个组内的本字段值必须相同! 61 | ``` 62 | 63 | 64 | 65 | # 关于默认值的说明: 66 | ``` 67 | searchType: 条件的搜索类型,若为空则为0,默认为模糊搜索方式; 68 | order: 条件的位置顺序,若为空则为0,默认为第一位; 69 | logicalType: 条件与其他条件的逻辑关系,若为空则为0,默认为或的关系; 70 | logicalTypeGroup:组括号与其他组括号的逻辑关系,若为空则为0,默认为或的关系; 71 | bracketsGroup:括号组的名称,若为空则默认为:DEFAULT_NO_GROUP,自己的组名要避免和默认组相同,除非你不指定组。 72 | 73 | ``` 74 | 75 | # 单条件搜索案例: 76 | - 1.最简单的NULL和NOT NULL查询 77 | ``` 78 | NULL条件: 查询email字段值为null的记录 79 | { 80 | "conditions": [ 81 | { 82 | "name":"email", 83 | "searchType": 8 84 | } 85 | ] 86 | } 87 | 终端生成SQL条件:where student0_.email is null ... 88 | NOT NULL条件:查询email字段值不为null的记录 89 | { 90 | "conditions": [ 91 | { 92 | "name":"email", 93 | "searchType": 9 94 | } 95 | ] 96 | } 97 | 终端生成SQL条件:where student0_.email is not null ... 98 | 99 | ``` 100 | 以下为测试图片: 101 | ![image](https://raw.githubusercontent.com/vip-efactory/ejpa-example/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/1-ISNULL-Query.png) 102 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/1-NOTNULL-Query.png) 103 | 104 | - 2.模糊查询,左模糊,右模糊 105 | ``` 106 | 全模糊查询:只要包含即可: 107 | { 108 | "conditions": [ 109 | { 110 | "name":"name", 111 | "searchType": 0, 112 | "val": "my" 113 | } 114 | ] 115 | } 116 | 117 | 左模糊查询:--以XXX结尾的 118 | { 119 | "conditions": [ 120 | { 121 | "name":"name", 122 | "searchType": 10, 123 | "val":"chuo" 124 | } 125 | ] 126 | } 127 | 128 | 右模糊查询:--以XXX开头的 129 | { 130 | "conditions": [ 131 | { 132 | "name":"name", 133 | "searchType": 11, 134 | "val": "joy" 135 | } 136 | ] 137 | } 138 | 终端SQL条件都是 139 | 【student0_.name like ?】 -----这是字符串类型的模糊查询 140 | 【where cast(student0_.age as char) like ?】 ----非字符串类型后台在数据库层面转换为字符串后进行模糊查询! 141 | ,都是?问号自身的值却是不同的,实现如下: 142 | // 0 或其他情况,则为模糊查询,FUZZY(0, "模糊查询"), 143 | fieldP = cb.like(root.get(key).as(String.class), "%" + startVal + "%"); 144 | // LEFT_LIKE(10, "左模糊查询"), 145 | fieldP = cb.like(root.get(key).as(String.class), "%" + startVal); 146 | // RIGHT_LIKE(11, "右模糊查询") 147 | fieldP = cb.like(root.get(key).as(String.class), startVal + "%"); 148 | ``` 149 | 以下为测试图片: 150 | 全模糊查询: 151 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/2-Fuzzy-Query.png) 152 | 左模糊查询: 153 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/2-LeftFuzzy-Query.png) 154 | 右模糊查询: 155 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/2-RightFuzzy-Query.png) 156 | 157 | - 3.等于查询、不等于查询 158 | ``` 159 | = 查询 160 | { 161 | "conditions": [ 162 | { 163 | "name":"age", 164 | "searchType": 1, 165 | "val": "45" 166 | } 167 | ] 168 | } 169 | 终端打印的SQL条件:where student0_.age=45 170 | 171 | != 查询 172 | { 173 | "conditions": [ 174 | { 175 | "name":"age", 176 | "searchType": 3, 177 | "val": "30" 178 | } 179 | ] 180 | } 181 | 终端打印的SQL条件:where student0_.age<>30 182 | 183 | ``` 184 | 以下为测试图片: 185 | = 查询: 186 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/3-EqualQuery.png) 187 | != 查询 188 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/3-NotEqualQuery.png) 189 | 190 | - 4.区间范围查询 191 | ``` 192 | { 193 | "conditions": [ 194 | { 195 | "name":"age", 196 | "searchType": 2, 197 | "val": "32", 198 | "val2":"36" 199 | } 200 | ] 201 | } 202 | 终端打印的SQL条件:where student0_.age between 32 and 36 203 | ``` 204 | 测试图片: 205 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/4-BetweenQuery.png) 206 | 207 | - 5.小于、小于等于、大于、大于等于 208 | ``` 209 | 小于条件: 210 | { 211 | "conditions": [ 212 | { 213 | "name":"age", 214 | "searchType": 4, 215 | "val": "34" 216 | } 217 | ] 218 | } 219 | 终端打印的SQL条件: where student0_.age<34 220 | 221 | 小于等于条件: 222 | { 223 | "conditions": [ 224 | { 225 | "name":"age", 226 | "searchType": 5, 227 | "val": "30" 228 | } 229 | ] 230 | } 231 | 终端打印的SQL条件:student0_.age<=30 232 | 233 | 大于条件: 234 | { 235 | "conditions": [ 236 | { 237 | "name":"age", 238 | "searchType": 6, 239 | "val": "34" 240 | } 241 | ] 242 | } 243 | 终端打印的SQL条件:where student0_.age>34 244 | 245 | 大于等于条件: 246 | { 247 | "conditions": [ 248 | { 249 | "name":"age", 250 | "searchType": 7, 251 | "val": "34" 252 | } 253 | ] 254 | } 255 | 终端打印的SQL条件:where student0_.age>=34 256 | ``` 257 | 测试图片: 258 | 小于条件: 259 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/5-LT-Query.png) 260 | 小于等于条件: 261 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/5-LE-Query.png) 262 | 大于条件: 263 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/5-GT-Query.png) 264 | 大于等于条件: 265 | ![image](https://github.com/vip-efactory/ejpa-example/raw/master/src/main/resources/static/usage/advQuery/%E5%8D%95%E6%9D%A1%E4%BB%B6%E6%9F%A5%E8%AF%A2/5-GE-Query.png) 266 | 267 | - 6 包含查询,in查询的值,支持的分隔符:中英文的逗号分号,和中文的顿号! 268 | ``` 269 | 小于条件: 270 | { 271 | "conditions": [ 272 | { 273 | "name":"height", 274 | "searchType": 12, 275 | "val": "165,170.0" 276 | } 277 | ] 278 | } 279 | 终端打印的SQL条件: where student0_.height in (165.0 , 170.0) 280 | ``` 281 | 282 | # 多条件搜索案例: 283 | - 多条件不包含括号组 284 | - 所有条件均为OR关系,即所有条件只要有一个即可 285 | ``` 286 | { 287 | "conditions": [ 288 | { 289 | "name":"name", 290 | "searchType": 0, 291 | "val": "chuo" 292 | }, 293 | { 294 | "name":"age", 295 | "searchType": 1, 296 | "val": "45" 297 | } 298 | ] 299 | } 300 | 终端打印的SQL条件:where student0_.name like ? or student0_.age=45 301 | 给name条件指定位置顺序: 302 | { 303 | "conditions": [ 304 | { 305 | "name":"name", 306 | "searchType": 0, 307 | "val": "chuo", 308 | "order":"2" 309 | }, 310 | { 311 | "name":"age", 312 | "searchType": 1, 313 | "val": "45" 314 | } 315 | ] 316 | } 317 | 终端打印的SQL条件:where student0_.age=45 or student0_.name like ? 318 | 可见name条件在age条件后面了! 319 | ``` 320 | 321 | - 所有条件均为AND关系,即所有条件都要满足 322 | ``` 323 | { 324 | "conditions": [ 325 | { 326 | "name":"name", 327 | "searchType": 0, 328 | "val": "chuo", 329 | "logicalType":1 330 | }, 331 | { 332 | "name":"age", 333 | "searchType": 1, 334 | "val": "45", 335 | "logicalType":1 336 | } 337 | ] 338 | } 339 | 终端打印的SQL条件:where student0_.age=45 and (student0_.name like ?) 340 | ``` 341 | 说明:上面两种应是最常见的方式。其实可以通过指定order和logicalType的值,实现很复杂的关系。例如:A =2 && B=3 || C=4 && D=5,当然这样的条件有没有效就是另外一回事了。 342 | 343 | - 多条件包含括号组 344 | - 包含一个组 345 | ``` 346 | 分组AND关系: 347 | { 348 | "conditions": [ 349 | { 350 | "name":"name", 351 | "searchType": 0, 352 | "val": "chuo", 353 | "logicalType":1 354 | }, 355 | { 356 | "name":"age", 357 | "searchType": 1, 358 | "val": "45", 359 | "bracketsGroup":"AAA", 360 | "logicalTypeGroup":1 361 | }, 362 | { 363 | "name":"id", 364 | "searchType": 1, 365 | "val": "3", 366 | "bracketsGroup":"AAA", 367 | "logicalTypeGroup":1 368 | } 369 | ] 370 | } 371 | 终端打印的SQL条件 :where (student0_.name like ?) and (student0_.id=3 or student0_.age=45) 372 | 373 | 分组OR关系: 374 | { 375 | "conditions": [ 376 | { 377 | "name":"name", 378 | "searchType": 0, 379 | "val": "chuo", 380 | "logicalType":1 381 | }, 382 | { 383 | "name":"age", 384 | "searchType": 1, 385 | "val": "45", 386 | "bracketsGroup":"AAA", 387 | "logicalTypeGroup":0 388 | }, 389 | { 390 | "name":"id", 391 | "searchType": 1, 392 | "val": "3", 393 | "bracketsGroup":"AAA", 394 | "logicalTypeGroup":0 395 | } 396 | ] 397 | } 398 | 终端打印的SQL条件 :where student0_.name like ? or student0_.id=3 or student0_.age=45 399 | 从此处看出,如果都是or的关系,就算有分组信息,最终实现也会被优化掉! 400 | ``` 401 | 402 | - 包含两个组 403 | ``` 404 | { 405 | "conditions": [ 406 | { 407 | "name":"age", 408 | "searchType": 1, 409 | "val": "45", 410 | "logicalTypeGroup":1 411 | }, 412 | { 413 | "name":"id", 414 | "searchType": 1, 415 | "val": "3", 416 | "logicalTypeGroup":1 417 | }, 418 | { 419 | "name":"sex", 420 | "searchType": 1, 421 | "val": "1", 422 | "bracketsGroup":"BBB", 423 | "logicalTypeGroup":1 424 | }, 425 | { 426 | "name":"email", 427 | "searchType": 8, 428 | "bracketsGroup":"BBB", 429 | "logicalTypeGroup":1 430 | } 431 | ] 432 | } 433 | 终端打印的SQL条件 :where (student0_.id=3 or student0_.age=45) and (student0_.email is null or student0_.sex=1) 434 | 说明:前2个条件没有指定组,则是默认组! 435 | 436 | 437 | 明确指定两个组的组名: 438 | { 439 | "conditions": [ 440 | { 441 | "name":"age", 442 | "searchType": 1, 443 | "val": "45", 444 | "bracketsGroup":"AAA", 445 | "logicalTypeGroup":1 446 | }, 447 | { 448 | "name":"id", 449 | "searchType": 1, 450 | "val": "3", 451 | "bracketsGroup":"AAA", 452 | "logicalTypeGroup":1 453 | }, 454 | { 455 | "name":"sex", 456 | "searchType": 1, 457 | "val": "1", 458 | "bracketsGroup":"BBB", 459 | "logicalTypeGroup":1 460 | }, 461 | { 462 | "name":"email", 463 | "searchType": 8, 464 | "bracketsGroup":"BBB", 465 | "logicalTypeGroup":1 466 | } 467 | ] 468 | } 469 | 终端打印的SQL条件:where (student0_.id=3 or student0_.age=45) and (student0_.sex=1 or student0_.email is null) 470 | 471 | ``` 472 | 473 | 其他说明: 理论上可以支持多分组,但是两个分组已经可以满足大多数的场景,若后面有需要,可以增加组顺序groupOrder属性来加强! 474 | -------------------------------------------------------------------------------- /docs/版本历史.md: -------------------------------------------------------------------------------- 1 | ejpa版本历史 2 | 3 | # V3.5.1 升级内容:2020-04-23 4 | - 放弃spring拦截器的模式来获取租户信息,测试发现有些请求拦截不到; 5 | 使用的原生的过滤器模式拦截租户信息。 6 | 7 | # V3.5.0 升级内容:2020-04-06 8 | - 增加多租户支持 9 | - 升级SpringDataJpa版本到2.2.6,2020-0401最新版 10 | 11 | # V3.4.0 升级内容:2020-03-08 12 | - 查询条件值进行类型转换处理,避免类型不匹配异常; 13 | - 增加IN查询方式的支持; 14 | - 新增对数字类型:byte,short,BigInteger,BigDecimal的支持; 15 | - 修正:区间范围查询如果结束值小于开始值查不到数据的问题; 16 | - 修正:模糊查询是整数时报错的问题;解决:所有模糊查询都转换为字符串类型的模糊查询! 17 | 18 | # V3.3.2 升级内容:2020-01-13 19 | - 属性检查增加创建组,以便使用时不需要指定太多参数; 20 | - 移除一些无用的代码; 21 | - 增强EPage的功能。 22 | 23 | # V3.3.1 升级内容:2020-01-12 24 | - 升级国际化组件版本至1.1.0 25 | 26 | # V3.3.0 升级内容:2020-01-10 27 | - 批量主键删除模板:使用id的Set集合来删除指定的实体,不使用数组防止存在重复的数据 28 | 29 | # V3.2.0 升级内容:2020-01-03 30 | - 简化jpa返回的分页数据结构 31 | 32 | # V3.1.0 升级内容:2019-12-24 33 | - 增强实体属性校验支持分组:新增使用默认组,更新使用Update组,Update组允许部分字段为空,但是若有值必须符合约束规则! 34 | 35 | # V3.0.2 升级内容:2019-12-22 36 | - 修复主键ID没有抽取完全情况 37 | - 优化CRUD,增加遗漏的检查逻辑 38 | 39 | # V3.0.1 升级内容:2019-12-21 40 | - 修复多分组情况下,默认组空条件导致的空指针的异常 41 | 42 | # V3.0.0 升级内容:2019-12-20 43 | - 高级查询支持更加复杂的查询方式 44 | - 实现基本的国际化信息 45 | - 实现对实体属性的前端约束检查 46 | 47 | # V2.0.0 升级内容:2019-12-09 48 | - 升级了依赖的版本到2019-12-9最新版; 49 | - 优化了请求的响应体R类; 50 | - 实现主键不在BaseEntity中定义,移植到子类中,以便具有更大的灵活性; 51 | - 修复了工具类中版本比较存在的潜在错误。 52 | 53 | # V1.0.0 内容:2019-07-13 54 | - 实现基础的CRUD功能; 55 | - 实现相对较弱高级查询功能; 56 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.6.RELEASE 9 | 10 | 11 | vip.efactory.ejpa.example 12 | ejpa-example 13 | 0.0.1-SNAPSHOT 14 | ejpa-example 15 | ejpa框架的是使用及测试项目 16 | 17 | 18 | 1.8 19 | 4.1.0 20 | 8.0.18 21 | 4.3.1 22 | 5.3.10 23 | 24 | 25 | 26 | 27 | 28 | vip.efactory 29 | ejpa-spring-boot-starter 30 | ${ejpa.version} 31 | pom 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | mysql 47 | mysql-connector-java 48 | runtime 49 | ${mysql.driver.version} 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-starter-test 87 | test 88 | 89 | 90 | org.junit.vintage 91 | junit-vintage-engine 92 | 93 | 94 | 95 | 96 | 97 | com.querydsl 98 | querydsl-apt 99 | ${querydsl.version} 100 | provided 101 | 102 | 103 | com.querydsl 104 | querydsl-jpa 105 | ${querydsl.version} 106 | 107 | 108 | cn.hutool 109 | hutool-all 110 | ${hutool.version} 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-maven-plugin 119 | 120 | 121 | 122 | 123 | com.mysema.maven 124 | apt-maven-plugin 125 | 1.1.3 126 | 127 | 128 | 129 | process 130 | 131 | 132 | target/generated-sources/java 133 | com.querydsl.apt.jpa.JPAAnnotationProcessor 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/EjpaExampleApplication.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 6 | 7 | @EnableJpaAuditing // 创建时间、创建人等字段的处理 8 | @SpringBootApplication 9 | public class EjpaExampleApplication { 10 | 11 | public static void main(String[] args) { 12 | // List classes = new LinkedList<>(); 13 | // classes.add(BaseEntity.class); 14 | // classes.add(Student.class); 15 | // Entityi18nUtil.copyToLocale(Entityi18nUtil.geni18nPropertiesFile("messages", classes)); 16 | SpringApplication.run(EjpaExampleApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/config/CacheObserveBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Component; 6 | import vip.efactory.ejpa.example.service.impl.CourseServiceImpl; 7 | import vip.efactory.ejpa.example.service.impl.TeacherServiceImpl; 8 | 9 | import javax.annotation.PostConstruct; 10 | 11 | /** 12 | * Description: 统一处理缓存观察者模式的。避免局部处理的循环引用 13 | * 14 | * @Author dbdu 15 | * @Date 2020-08-24 16 | */ 17 | @AllArgsConstructor 18 | @Component 19 | @Slf4j 20 | public class CacheObserveBeanPostProcessor { 21 | // course ,teacher组件的缓存处理 22 | CourseServiceImpl courseServiceImpl; 23 | TeacherServiceImpl teacherServiceImpl; 24 | 25 | 26 | private void init4course() { 27 | log.info("开始注册course的观察者..."); 28 | courseServiceImpl.registObservers(teacherServiceImpl); 29 | } 30 | 31 | @PostConstruct 32 | private void initCacheObserve() { 33 | init4course(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/config/DataSourceBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.boot.jdbc.DataSourceBuilder; 6 | import org.springframework.stereotype.Component; 7 | import vip.efactory.ejpa.example.entity.SysTenant; 8 | import vip.efactory.ejpa.example.service.ISysTenantService; 9 | import vip.efactory.ejpa.tenant.database.TenantDataSourceProvider; 10 | 11 | import javax.annotation.PostConstruct; 12 | import javax.sql.DataSource; 13 | import java.util.List; 14 | 15 | /** 16 | * 此处这么写是为了破解循环引用,MultiTenantJpaConfiguration与ISystemTenantService相互依赖 17 | */ 18 | @AllArgsConstructor 19 | @Component 20 | @Slf4j 21 | public class DataSourceBeanPostProcessor { 22 | private ISysTenantService tenantService; 23 | 24 | @PostConstruct 25 | public void init() { 26 | // 获取数据库里所有的租户信息 27 | log.info("多租户的数据源初始化开始..."); 28 | List tenantList = (List) tenantService.findAll(); 29 | // 初始化所有租户的数据源 30 | tenantList.forEach(tenant -> { 31 | DataSource dataSource = DataSourceBuilder.create() 32 | .url(tenant.getJdbcUrl()) 33 | .username(tenant.getDbUsername()) 34 | .password(tenant.getDbPassword()) 35 | .driverClassName(tenant.getDriverClassName()) 36 | .build(); 37 | TenantDataSourceProvider.addDataSource(tenant.getId().toString(), dataSource); // 放入数据源集合中 38 | log.info("租户{}的数据源初始化完成!", tenant.getId()); 39 | }); 40 | log.info("多租户的数据源初始化结束"); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/config/MultiTenantJpaConfiguration.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import org.hibernate.MultiTenancyStrategy; 5 | import org.hibernate.SessionFactory; 6 | import org.hibernate.cfg.Environment; 7 | import org.hibernate.context.spi.CurrentTenantIdentifierResolver; 8 | import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; 9 | import org.hibernate.tool.schema.Action; 10 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 11 | import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | import org.springframework.boot.jdbc.DataSourceBuilder; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Primary; 17 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 18 | import org.springframework.orm.hibernate5.HibernateTransactionManager; 19 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 20 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 21 | import org.springframework.transaction.PlatformTransactionManager; 22 | import org.springframework.transaction.annotation.EnableTransactionManagement; 23 | import vip.efactory.ejpa.example.entity.SysTenant; 24 | import vip.efactory.ejpa.tenant.database.MultiTenantConnectionProviderImpl; 25 | import vip.efactory.ejpa.tenant.database.MultiTenantIdentifierResolver; 26 | import vip.efactory.ejpa.tenant.database.TenantDataSourceProvider; 27 | import vip.efactory.ejpa.tenant.identifier.TenantConstants; 28 | 29 | import javax.annotation.PostConstruct; 30 | import javax.persistence.EntityManagerFactory; 31 | import java.util.LinkedHashMap; 32 | import java.util.Map; 33 | 34 | @AllArgsConstructor 35 | @Configuration 36 | @EnableConfigurationProperties({JpaProperties.class}) 37 | @EnableTransactionManagement(proxyTargetClass = true) 38 | @EnableJpaRepositories(basePackages = {"vip.efactory.ejpa.example.repository"}, transactionManagerRef = "txManager") 39 | public class MultiTenantJpaConfiguration { 40 | 41 | private JpaProperties jpaProperties; // yml文件中的jpa配置 42 | private DataSourceProperties dataSourceProperties; // yml配置文件里的数据源,也就是租户数据表所在的数据源 43 | 44 | /** 45 | * 初始化所有租户的数据源 46 | */ 47 | @PostConstruct 48 | public void initDataSources() { 49 | // 先初始化租户表所在的数据源,然后从租户表中读取其他租户的数据源然后再进行初始化,详见:DataSourceBeanPostProcessor类 50 | DataSourceBuilder factory = DataSourceBuilder.create() 51 | .url(dataSourceProperties.getUrl()) 52 | .username(dataSourceProperties.getUsername()) 53 | .password(dataSourceProperties.getPassword()) 54 | .driverClassName(dataSourceProperties.getDriverClassName()); 55 | TenantDataSourceProvider.addDataSource(TenantConstants.DEFAULT_TENANT_ID.toString(), factory.build()); // 放入数据源集合中 56 | } 57 | 58 | @Bean 59 | public MultiTenantConnectionProvider multiTenantConnectionProvider() { 60 | return new MultiTenantConnectionProviderImpl(); 61 | } 62 | 63 | @Bean 64 | public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() { 65 | return new MultiTenantIdentifierResolver(); 66 | } 67 | 68 | @Bean 69 | public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(MultiTenantConnectionProvider multiTenantConnectionProvider, 70 | CurrentTenantIdentifierResolver currentTenantIdentifierResolver) { 71 | 72 | Map hibernateProps = new LinkedHashMap<>(); 73 | hibernateProps.putAll(this.jpaProperties.getProperties()); 74 | hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE); // 使用基于独立数据库的多租户模式 75 | hibernateProps.put(Environment.PHYSICAL_NAMING_STRATEGY, "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); //属性及column命名策略 76 | hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider); 77 | hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver); 78 | hibernateProps.put(Environment.HBM2DDL_AUTO, Action.UPDATE); // 自动更新表结构,仅默认数据源有效且控制台会报警告可以不用管! 79 | hibernateProps.put(Environment.SHOW_SQL, true); // 显示SQL 80 | hibernateProps.put(Environment.FORMAT_SQL, true); // 格式化SQL 81 | 82 | // No dataSource is set to resulting entityManagerFactoryBean 83 | LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean(); 84 | 85 | result.setPackagesToScan(new String[]{SysTenant.class.getPackage().getName()}); 86 | result.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); 87 | result.setJpaPropertyMap(hibernateProps); 88 | 89 | return result; 90 | } 91 | 92 | @Bean 93 | @Primary // 注意我们自己定义的bean,最好都加此注解,防止与自动配置的重复而不知道如何选择 94 | public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) { 95 | return entityManagerFactoryBean.getObject(); 96 | } 97 | 98 | @Bean 99 | @Primary // 注意我们自己定义的bean,最好都加此注解,防止与自动配置的重复而不知道如何选择 100 | public PlatformTransactionManager txManager(EntityManagerFactory entityManagerFactory) { 101 | SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); 102 | HibernateTransactionManager result = new HibernateTransactionManager(); 103 | result.setAutodetectDataSource(false); // 不自动检测数据源 104 | result.setSessionFactory(sessionFactory); 105 | result.setRollbackOnCommitFailure(true); 106 | return result; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/config/RedisTemplateConfiguration.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.config; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.codec.digest.DigestUtils; 7 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 8 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; 9 | import org.springframework.cache.Cache; 10 | import org.springframework.cache.annotation.CachingConfigurerSupport; 11 | import org.springframework.cache.annotation.EnableCaching; 12 | import org.springframework.cache.interceptor.CacheErrorHandler; 13 | import org.springframework.cache.interceptor.KeyGenerator; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.data.redis.connection.RedisConnectionFactory; 17 | import org.springframework.data.redis.core.*; 18 | import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; 19 | import org.springframework.data.redis.serializer.StringRedisSerializer; 20 | 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * Redis 配置类 26 | */ 27 | @Slf4j 28 | @EnableCaching 29 | @Configuration 30 | @RequiredArgsConstructor 31 | @AutoConfigureBefore(RedisAutoConfiguration.class) 32 | public class RedisTemplateConfiguration extends CachingConfigurerSupport { 33 | 34 | private final RedisConnectionFactory factory; 35 | 36 | @Bean 37 | public RedisTemplate redisTemplate() { 38 | RedisTemplate redisTemplate = new RedisTemplate<>(); 39 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 40 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 41 | redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); 42 | redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); 43 | redisTemplate.setConnectionFactory(factory); 44 | return redisTemplate; 45 | } 46 | 47 | @Bean 48 | public HashOperations hashOperations(RedisTemplate redisTemplate) { 49 | return redisTemplate.opsForHash(); 50 | } 51 | 52 | @Bean 53 | public ValueOperations valueOperations(RedisTemplate redisTemplate) { 54 | return redisTemplate.opsForValue(); 55 | } 56 | 57 | @Bean 58 | public ListOperations listOperations(RedisTemplate redisTemplate) { 59 | return redisTemplate.opsForList(); 60 | } 61 | 62 | @Bean 63 | public SetOperations setOperations(RedisTemplate redisTemplate) { 64 | return redisTemplate.opsForSet(); 65 | } 66 | 67 | @Bean 68 | public ZSetOperations zSetOperations(RedisTemplate redisTemplate) { 69 | return redisTemplate.opsForZSet(); 70 | } 71 | 72 | 73 | /** 74 | * 自定义缓存key生成策略,默认将使用该策略 75 | */ 76 | @Bean 77 | @Override 78 | public KeyGenerator keyGenerator() { 79 | return (target, method, params) -> { 80 | Map container = new HashMap<>(3); 81 | Class targetClassClass = target.getClass(); 82 | // 类地址 83 | container.put("class", targetClassClass.toGenericString()); 84 | // 方法名称 85 | container.put("methodName", method.getName()); 86 | // 包名称 87 | container.put("package", targetClassClass.getPackage()); 88 | // 参数列表 89 | for (int i = 0; i < params.length; i++) { 90 | container.put(String.valueOf(i), params[i]); 91 | } 92 | // 转为JSON字符串 93 | String jsonString = JSONUtil.toJsonStr(container); 94 | // 做SHA256 Hash计算,得到一个SHA256摘要作为Key 95 | return DigestUtils.sha256Hex(jsonString); 96 | }; 97 | } 98 | 99 | @Bean 100 | @Override 101 | public CacheErrorHandler errorHandler() { 102 | // 异常处理,当Redis发生异常时,打印日志,但是程序正常走 103 | log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); 104 | return new CacheErrorHandler() { 105 | @Override 106 | public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { 107 | log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); 108 | } 109 | 110 | @Override 111 | public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { 112 | log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); 113 | } 114 | 115 | @Override 116 | public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { 117 | log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); 118 | } 119 | 120 | @Override 121 | public void handleCacheClearError(RuntimeException e, Cache cache) { 122 | log.error("Redis occur handleCacheClearError:", e); 123 | } 124 | }; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/controller/CourseController.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.controller; 2 | 3 | import io.swagger.annotations.ApiOperation; 4 | import io.swagger.annotations.ApiParam; 5 | import org.springframework.beans.BeanUtils; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.data.web.PageableDefault; 9 | import org.springframework.web.bind.annotation.*; 10 | import vip.efactory.common.base.page.EPage; 11 | import vip.efactory.common.base.utils.R; 12 | import vip.efactory.ejpa.base.controller.BaseController; 13 | import vip.efactory.ejpa.base.entity.BaseSearchEntity; 14 | import vip.efactory.ejpa.example.entity.Course; 15 | import vip.efactory.ejpa.example.service.ICourseService; 16 | 17 | @RestController 18 | @RequestMapping("/course") 19 | @SuppressWarnings("all") 20 | public class CourseController extends BaseController { 21 | 22 | /** 23 | * Description: 默认的分页与排序 24 | * 25 | * @param page 分页参数对象 26 | * @return vip.efactory.ejpa.utils.R 27 | * @author dbdu 28 | */ 29 | @Override 30 | @ApiOperation(value = "获取分页数据", notes = "默认每页25条记录,id字段降序") 31 | @RequestMapping(value = "/page", method = {RequestMethod.GET}) 32 | public R getByPage(@PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 33 | return super.getByPage(page); 34 | } 35 | 36 | /** 37 | * Description: 高级查询 38 | * 39 | * @param baseSearchEntity 含有高级查询条件 40 | * @param page 分页参数对象 41 | * @return vip.efactory.ejpa.utils.R 42 | * @author dbdu 43 | */ 44 | @ApiOperation(value = "多条件组合查询,返回分页数据", notes = "默认每页25条记录,id字段降序") 45 | @RequestMapping(value = "/advanced/query", method = {RequestMethod.POST}) 46 | public R advancedQuery(@RequestBody BaseSearchEntity baseSearchEntity, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 47 | Course entity = new Course(); 48 | BeanUtils.copyProperties(baseSearchEntity, entity); 49 | return super.advancedQueryByPage(page, entity); 50 | } 51 | 52 | /** 53 | * Description:多字段模糊查询,例如: 54 | * http://frms.ddbin.com:8080/carton/fuzzy?fields=name,version&q=BB 55 | * 56 | * @param page 分页参数对象 57 | * @param q 模糊查询的值 58 | * @param fields 要查询的字段 59 | * @return vip.efactory.ejpa.utils.R 60 | * @author dbdu 61 | */ 62 | @ApiOperation(value = "多字段模糊查询,例如:q=abc&fields=name,address,desc", notes = "多个字段模糊匹配") 63 | @RequestMapping(value = "/fuzzy", method = {RequestMethod.GET}) 64 | public R getByPage(@RequestParam String q, @RequestParam String fields, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 65 | return super.queryMultiField(q, fields, page); 66 | } 67 | 68 | 69 | /** 70 | * Description:使用id来获取实体 71 | * 72 | * @param id 主键 73 | * @return vip.efactory.ejpa.utils.R 74 | * @author dbdu 75 | */ 76 | @Override 77 | @GetMapping("/{id}") 78 | @ApiOperation(value = "依据Id来获取对应的记录", notes = "依据Id来获取对应的记录") 79 | public R getById(@PathVariable("id") Long id) { 80 | return super.getById(id); 81 | } 82 | 83 | 84 | /** 85 | * Description:保存实体 86 | * 87 | * @param entity 要保存的对象实体 88 | * @return vip.efactory.ejpa.utils.R 89 | * @author dbdu 90 | */ 91 | @Override 92 | @PostMapping 93 | @ApiOperation(value = "保存记录", notes = "保存学生实体") 94 | public R save(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Course entity) { 95 | return super.save(entity); 96 | } 97 | 98 | /** 99 | * Description:更新 100 | * 101 | * @param entity 更新对象 102 | * @return vip.efactory.ejpa.utils.R 103 | * @author dbdu 104 | */ 105 | @Override 106 | @PutMapping 107 | @ApiOperation(value = "依据Id来更新对应的记录", notes = "依据Id来更新对应的记录,属性值为空则不更新数据表中已有的数据") 108 | public R updateById(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Course entity) { 109 | return super.updateById(entity); 110 | } 111 | 112 | /** 113 | * Description: 依据id来删除实体 114 | * 115 | * @param id 主键 116 | * @return vip.efactory.ejpa.utils.R 117 | * @author dbdu 118 | */ 119 | @Override 120 | @DeleteMapping("/{id}") 121 | @ApiOperation(value = "依据Id来删除对应的记录", notes = "依据Id来删除对应的记录") 122 | public R deleteById(@PathVariable Long id) { 123 | return super.deleteById(id); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/controller/StudentController.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.controller; 2 | 3 | import io.swagger.annotations.ApiOperation; 4 | import io.swagger.annotations.ApiParam; 5 | import org.springframework.beans.BeanUtils; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.data.web.PageableDefault; 9 | import org.springframework.web.bind.annotation.*; 10 | import vip.efactory.common.base.page.EPage; 11 | import vip.efactory.common.base.utils.R; 12 | import vip.efactory.ejpa.base.controller.BaseController; 13 | import vip.efactory.ejpa.base.entity.BaseSearchEntity; 14 | import vip.efactory.ejpa.example.entity.Student; 15 | import vip.efactory.ejpa.example.service.IStudentService; 16 | 17 | @RestController 18 | @RequestMapping("/student") 19 | @SuppressWarnings("all") 20 | public class StudentController extends BaseController { 21 | 22 | /** 23 | * Description: 默认的分页与排序 24 | * 25 | * @param page 分页参数对象 26 | * @return vip.efactory.ejpa.utils.R 27 | * @author dbdu 28 | */ 29 | @Override 30 | @ApiOperation(value = "获取分页数据", notes = "默认每页25条记录,id字段降序") 31 | @RequestMapping(value = "/page", method = {RequestMethod.GET}) 32 | public R getByPage(@PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 33 | return super.getByPage(page); 34 | } 35 | 36 | /** 37 | * Description: 高级查询 38 | * 39 | * @param baseSearchEntity 含有高级查询条件 40 | * @param page 分页参数对象 41 | * @return vip.efactory.ejpa.utils.R 42 | * @author dbdu 43 | */ 44 | @ApiOperation(value = "多条件组合查询,返回分页数据", notes = "默认每页25条记录,id字段降序") 45 | @RequestMapping(value = "/advanced/query", method = {RequestMethod.POST}) 46 | public R advancedQuery(@RequestBody BaseSearchEntity baseSearchEntity, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 47 | Student entity = new Student(); 48 | BeanUtils.copyProperties(baseSearchEntity, entity); 49 | return super.advancedQueryByPage(page, entity); 50 | } 51 | 52 | /** 53 | * Description:多字段模糊查询,例如: 54 | * http://frms.ddbin.com:8080/carton/fuzzy?fields=name,version&q=BB 55 | * 56 | * @param page 分页参数对象 57 | * @param q 模糊查询的值 58 | * @param fields 要查询的字段 59 | * @return vip.efactory.ejpa.utils.R 60 | * @author dbdu 61 | */ 62 | @ApiOperation(value = "多字段模糊查询,例如:q=abc&fields=name,address,desc", notes = "多个字段模糊匹配") 63 | @RequestMapping(value = "/fuzzy", method = {RequestMethod.GET}) 64 | public R getByPage(@RequestParam String q, @RequestParam String fields, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 65 | return super.queryMultiField(q, fields, page); 66 | } 67 | 68 | 69 | /** 70 | * Description:使用id来获取实体 71 | * 72 | * @param id 主键 73 | * @return vip.efactory.ejpa.utils.R 74 | * @author dbdu 75 | */ 76 | @Override 77 | @GetMapping("/{id}") 78 | @ApiOperation(value = "依据Id来获取对应的记录", notes = "依据Id来获取对应的记录") 79 | public R getById(@PathVariable("id") Long id) { 80 | return super.getById(id); 81 | } 82 | 83 | 84 | /** 85 | * Description:保存实体 86 | * 87 | * @param entity 要保存的对象实体 88 | * @return vip.efactory.ejpa.utils.R 89 | * @author dbdu 90 | */ 91 | @Override 92 | @PostMapping 93 | @ApiOperation(value = "保存记录", notes = "保存学生实体") 94 | public R save( @RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Student entity) { 95 | return super.save(entity); 96 | } 97 | 98 | /** 99 | * Description:更新 100 | * 101 | * @param entity 更新对象 102 | * @return vip.efactory.ejpa.utils.R 103 | * @author dbdu 104 | */ 105 | @Override 106 | @PutMapping 107 | @ApiOperation(value = "依据Id来更新对应的记录", notes = "依据Id来更新对应的记录,属性值为空则不更新数据表中已有的数据") 108 | public R updateById(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Student entity) { 109 | return super.updateById(entity); 110 | } 111 | 112 | /** 113 | * Description: 依据id来删除实体 114 | * 115 | * @param id 主键 116 | * @return vip.efactory.ejpa.utils.R 117 | * @author dbdu 118 | */ 119 | @Override 120 | @DeleteMapping("/{id}") 121 | @ApiOperation(value = "依据Id来删除对应的记录", notes = "依据Id来删除对应的记录") 122 | public R deleteById(@PathVariable Long id) { 123 | return super.deleteById(id); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/controller/SysTenantController.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.controller; 2 | 3 | import io.swagger.annotations.ApiOperation; 4 | import io.swagger.annotations.ApiParam; 5 | import org.springframework.beans.BeanUtils; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.data.web.PageableDefault; 9 | import org.springframework.web.bind.annotation.*; 10 | import vip.efactory.common.base.utils.R; 11 | import vip.efactory.ejpa.base.controller.BaseController; 12 | import vip.efactory.ejpa.base.entity.BaseSearchEntity; 13 | import vip.efactory.ejpa.example.entity.SysTenant; 14 | import vip.efactory.ejpa.example.service.ISysTenantService; 15 | 16 | @RestController 17 | @RequestMapping("/tenant") 18 | public class SysTenantController extends BaseController { 19 | 20 | /** 21 | * Description: 默认的分页与排序 22 | * 23 | * @param page 分页参数对象 24 | * @return vip.efactory.ejpa.utils.R 25 | * @author dbdu 26 | */ 27 | @Override 28 | @ApiOperation(value = "获取分页数据", notes = "默认每页25条记录,id字段降序") 29 | @RequestMapping(value = "/page", method = {RequestMethod.GET}) 30 | public R getByPage(@PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 31 | return super.getByPage(page); 32 | } 33 | 34 | /** 35 | * Description: 高级查询 36 | * 37 | * @param baseSearchEntity 含有高级查询条件 38 | * @param page 分页参数对象 39 | * @return vip.efactory.ejpa.utils.R 40 | * @author dbdu 41 | */ 42 | @ApiOperation(value = "多条件组合查询,返回分页数据", notes = "默认每页25条记录,id字段降序") 43 | @RequestMapping(value = "/advanced/query", method = {RequestMethod.POST}) 44 | public R advancedQuery(@RequestBody BaseSearchEntity baseSearchEntity, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 45 | SysTenant entity = new SysTenant(); 46 | BeanUtils.copyProperties(baseSearchEntity, entity); 47 | return super.advancedQueryByPage(page, entity); 48 | } 49 | 50 | /** 51 | * Description:多字段模糊查询,例如: 52 | * http://frms.ddbin.com:8080/carton/fuzzy?fields=name,version&q=BB 53 | * 54 | * @param page 分页参数对象 55 | * @param q 模糊查询的值 56 | * @param fields 要查询的字段 57 | * @return vip.efactory.ejpa.utils.R 58 | * @author dbdu 59 | */ 60 | @ApiOperation(value = "多字段模糊查询,例如:q=abc&fields=name,address,desc", notes = "多个字段模糊匹配") 61 | @RequestMapping(value = "/fuzzy", method = {RequestMethod.GET}) 62 | public R getByPage(@RequestParam String q, @RequestParam String fields, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 63 | return super.queryMultiField(q, fields, page); 64 | } 65 | 66 | 67 | /** 68 | * Description:使用id来获取实体 69 | * 70 | * @param id 主键 71 | * @return vip.efactory.ejpa.utils.R 72 | * @author dbdu 73 | */ 74 | @Override 75 | @GetMapping("/{id}") 76 | @ApiOperation(value = "依据Id来获取对应的记录", notes = "依据Id来获取对应的记录") 77 | public R getById(@PathVariable("id") Long id) { 78 | return super.getById(id); 79 | } 80 | 81 | 82 | /** 83 | * Description:保存实体 84 | * 85 | * @param entity 要保存的对象实体 86 | * @return vip.efactory.ejpa.utils.R 87 | * @author dbdu 88 | */ 89 | @Override 90 | @PostMapping 91 | @ApiOperation(value = "保存记录", notes = "保存学生实体") 92 | public R save(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) SysTenant entity) { 93 | return super.save(entity); 94 | } 95 | 96 | /** 97 | * Description:更新 98 | * 99 | * @param entity 更新对象 100 | * @return vip.efactory.ejpa.utils.R 101 | * @author dbdu 102 | */ 103 | @Override 104 | @PutMapping 105 | @ApiOperation(value = "依据Id来更新对应的记录", notes = "依据Id来更新对应的记录,属性值为空则不更新数据表中已有的数据") 106 | public R updateById(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) SysTenant entity) { 107 | return super.updateById(entity); 108 | } 109 | 110 | /** 111 | * Description: 依据id来删除实体 112 | * 113 | * @param id 主键 114 | * @return vip.efactory.ejpa.utils.R 115 | * @author dbdu 116 | */ 117 | @Override 118 | @DeleteMapping("/{id}") 119 | @ApiOperation(value = "依据Id来删除对应的记录", notes = "依据Id来删除对应的记录") 120 | public R deleteById(@PathVariable Long id) { 121 | return super.deleteById(id); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/controller/TeacherController.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.controller; 2 | 3 | import io.swagger.annotations.ApiOperation; 4 | import io.swagger.annotations.ApiParam; 5 | import org.springframework.beans.BeanUtils; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.data.web.PageableDefault; 9 | import org.springframework.web.bind.annotation.*; 10 | import vip.efactory.common.base.page.EPage; 11 | import vip.efactory.common.base.utils.R; 12 | import vip.efactory.ejpa.base.controller.BaseController; 13 | import vip.efactory.ejpa.base.entity.BaseSearchEntity; 14 | import vip.efactory.ejpa.example.entity.Teacher; 15 | import vip.efactory.ejpa.example.service.ITeacherService; 16 | 17 | @RestController 18 | @RequestMapping("/teacher") 19 | @SuppressWarnings("all") 20 | public class TeacherController extends BaseController { 21 | 22 | /** 23 | * Description: 默认的分页与排序 24 | * 25 | * @param page 分页参数对象 26 | * @return vip.efactory.ejpa.utils.R 27 | * @author dbdu 28 | */ 29 | @Override 30 | @ApiOperation(value = "获取分页数据", notes = "默认每页25条记录,id字段降序") 31 | @RequestMapping(value = "/page", method = {RequestMethod.GET}) 32 | public R getByPage(@PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 33 | return super.getByPage(page); 34 | } 35 | 36 | /** 37 | * Description: 高级查询 38 | * 39 | * @param baseSearchEntity 含有高级查询条件 40 | * @param page 分页参数对象 41 | * @return vip.efactory.ejpa.utils.R 42 | * @author dbdu 43 | */ 44 | @ApiOperation(value = "多条件组合查询,返回分页数据", notes = "默认每页25条记录,id字段降序") 45 | @RequestMapping(value = "/advanced/query", method = {RequestMethod.POST}) 46 | public R advancedQuery(@RequestBody BaseSearchEntity baseSearchEntity, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 47 | Teacher entity = new Teacher(); 48 | BeanUtils.copyProperties(baseSearchEntity, entity); 49 | return super.advancedQueryByPage(page, entity); 50 | } 51 | 52 | /** 53 | * Description:多字段模糊查询,例如: 54 | * http://frms.ddbin.com:8080/carton/fuzzy?fields=name,version&q=BB 55 | * 56 | * @param page 分页参数对象 57 | * @param q 模糊查询的值 58 | * @param fields 要查询的字段 59 | * @return vip.efactory.ejpa.utils.R 60 | * @author dbdu 61 | */ 62 | @ApiOperation(value = "多字段模糊查询,例如:q=abc&fields=name,address,desc", notes = "多个字段模糊匹配") 63 | @RequestMapping(value = "/fuzzy", method = {RequestMethod.GET}) 64 | public R getByPage(@RequestParam String q, @RequestParam String fields, @PageableDefault(value = 25, sort = {"id"}, direction = Sort.Direction.DESC) Pageable page) { 65 | return super.queryMultiField(q, fields, page); 66 | } 67 | 68 | 69 | /** 70 | * Description:使用id来获取实体 71 | * 72 | * @param id 主键 73 | * @return vip.efactory.ejpa.utils.R 74 | * @author dbdu 75 | */ 76 | @Override 77 | @GetMapping("/{id}") 78 | @ApiOperation(value = "依据Id来获取对应的记录", notes = "依据Id来获取对应的记录") 79 | public R getById(@PathVariable("id") Long id) { 80 | return super.getById(id); 81 | } 82 | 83 | 84 | /** 85 | * Description:保存实体 86 | * 87 | * @param entity 要保存的对象实体 88 | * @return vip.efactory.ejpa.utils.R 89 | * @author dbdu 90 | */ 91 | @Override 92 | @PostMapping 93 | @ApiOperation(value = "保存记录", notes = "保存学生实体") 94 | public R save(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Teacher entity) { 95 | return super.save(entity); 96 | } 97 | 98 | /** 99 | * Description:更新 100 | * 101 | * @param entity 更新对象 102 | * @return vip.efactory.ejpa.utils.R 103 | * @author dbdu 104 | */ 105 | @Override 106 | @PutMapping 107 | @ApiOperation(value = "依据Id来更新对应的记录", notes = "依据Id来更新对应的记录,属性值为空则不更新数据表中已有的数据") 108 | public R updateById(@RequestBody @ApiParam(name = "entity", value = "Json格式", required = true) Teacher entity) { 109 | return super.updateById(entity); 110 | } 111 | 112 | /** 113 | * Description: 依据id来删除实体 114 | * 115 | * @param id 主键 116 | * @return vip.efactory.ejpa.utils.R 117 | * @author dbdu 118 | */ 119 | @Override 120 | @DeleteMapping("/{id}") 121 | @ApiOperation(value = "依据Id来删除对应的记录", notes = "依据Id来删除对应的记录") 122 | public R deleteById(@PathVariable Long id) { 123 | return super.deleteById(id); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/entity/Course.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.hibernate.validator.constraints.Length; 7 | import org.hibernate.validator.constraints.Range; 8 | import vip.efactory.common.base.valid.Update; 9 | import vip.efactory.ejpa.base.entity.BaseEntity; 10 | 11 | import javax.persistence.*; 12 | import javax.validation.constraints.*; 13 | import javax.validation.groups.Default; 14 | import java.math.BigDecimal; 15 | import java.time.LocalDate; 16 | import java.util.Date; 17 | 18 | /** 19 | * 学科表,用来测试接口是否正常 20 | * 对于校验注解,没有指定groups属性的,则默认是Default.class组, 21 | * 控制器里在save方法是使用默认组, 22 | * updateById,使用Update.class组,更新时允许部分字段为空,但是若有值则校验属性值的合法性 23 | */ 24 | @Setter 25 | @Getter 26 | @Entity 27 | public class Course extends BaseEntity { 28 | 29 | @Id 30 | @GeneratedValue(strategy = GenerationType.IDENTITY) 31 | @NotNull(message = "id {property.not.allow.empty}", groups = Update.class) // 意味着,updateById更新时id不允许为空 32 | private Long id; 33 | 34 | // 不论新增和更新都应符合要求 35 | @Length(min = 2, max = 32, groups = {Update.class, Default.class}) 36 | // 意思是,新增时不允许为空,updateById更新时可为空! 37 | @NotBlank 38 | private String name; 39 | 40 | // 时长,例如:30 课时 41 | @Range(min = 0, max = 150, groups = {Update.class, Default.class}) 42 | private Integer duration; 43 | 44 | // 学费 ,例如 3500.40元 45 | @PositiveOrZero 46 | private BigDecimal coins; 47 | 48 | // 本期课程开设日期 49 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 50 | private LocalDate openingDate; 51 | 52 | // @OneToOne(targetEntity = Teacher.class,mappedBy = "course") 53 | // private Teacher teacher; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/entity/Student.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.hibernate.validator.constraints.Length; 7 | import org.hibernate.validator.constraints.Range; 8 | import vip.efactory.common.base.valid.Update; 9 | import vip.efactory.ejpa.base.entity.BaseEntity; 10 | 11 | import javax.persistence.*; 12 | import javax.validation.constraints.*; 13 | import javax.validation.groups.Default; 14 | import java.time.LocalDate; 15 | import java.util.Date; 16 | import java.util.HashSet; 17 | import java.util.Set; 18 | 19 | /** 20 | * 学生表,用来测试接口是否正常 21 | * 对于校验注解,没有指定groups属性的,则默认是Default.class组, 22 | * 控制器里在save方法是使用默认组, 23 | * updateById,使用Update.class组,更新时允许部分字段为空,但是若有值则校验属性值的合法性 24 | */ 25 | @Setter 26 | @Getter 27 | @Entity 28 | public class Student extends BaseEntity { 29 | 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.IDENTITY) 32 | @NotNull(message = "id {property.not.allow.empty}", groups = Update.class) // 意味着,updateById更新时id不允许为空 33 | private Long id; 34 | 35 | // 不论新增和更新都应符合要求 36 | @Length(min = 4, max = 8, message = "{Student.name} {property.length.in.between}", groups = {Update.class, Default.class}) 37 | // 意思是,新增时不允许为空,updateById更新时可为空! 38 | @NotBlank(message = "{Student.name} {property.not.allow.empty}") 39 | private String name; 40 | 41 | @Range(min = 0, max = 2, message = "{Student.sex} {property.value.in.range}", groups = {Update.class, Default.class}) 42 | private Integer sex; // 0 未知;1 男;2 女 43 | 44 | @PositiveOrZero(message = "{Student.age} {property.not.allow.negative}", groups = {Update.class, Default.class}) 45 | private Integer age; 46 | 47 | /** 48 | * 爱好,测试不使用国际化文件,直接硬编码返回! 49 | */ 50 | @NotBlank(message = "爱好属性信息不允许为空") 51 | private String hobby; 52 | 53 | /** 54 | * 身高,单位cm,例如:180.6cm 55 | */ 56 | @Max(value = 300, message = "{Student.height}{property.value.should.lt.max}", groups = {Update.class, Default.class}) 57 | private Double height; 58 | 59 | @Email(message = "{Student.email}{property.format.error}", groups = {Update.class, Default.class}) 60 | private String email; 61 | 62 | @Past(message = "{Student.birthday}{property.value.only.past}", groups = {Update.class, Default.class}) 63 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 64 | private LocalDate birthday; 65 | 66 | @OneToMany(targetEntity = Teacher.class) 67 | @JoinTable( 68 | name = "tbl_stu_teacher", 69 | joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"), 70 | inverseJoinColumns = @JoinColumn(name = "teacher_id", referencedColumnName = "id", unique = true) 71 | ) 72 | private Set teachers = new HashSet<>(); // 一个学生关联多个老师 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/entity/SysTenant.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import vip.efactory.common.base.valid.Update; 6 | import vip.efactory.ejpa.base.entity.TenantEntity; 7 | 8 | import javax.persistence.Entity; 9 | import javax.persistence.GeneratedValue; 10 | import javax.persistence.GenerationType; 11 | import javax.persistence.Id; 12 | import javax.validation.constraints.NotNull; 13 | 14 | /** 15 | * 多租户表,用来管理系统中的多租户数据源信息 16 | */ 17 | @Setter 18 | @Getter 19 | @Entity 20 | public class SysTenant extends TenantEntity { 21 | 22 | /** 23 | * 租户ID,也是主键,默认为0L 24 | */ 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.IDENTITY) 27 | @NotNull(message = "id {property.not.allow.empty}", groups = Update.class) // 意味着,updateById更新时id不允许为空 28 | private Long id; 29 | 30 | /** 31 | * 租户状态: 0 --正常可用;1--禁用; 32 | */ 33 | private Integer status; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/entity/Teacher.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.hibernate.validator.constraints.Length; 7 | import vip.efactory.common.base.valid.Update; 8 | import vip.efactory.ejpa.base.entity.BaseEntity; 9 | 10 | import javax.persistence.*; 11 | import javax.validation.constraints.*; 12 | import javax.validation.groups.Default; 13 | import java.util.Date; 14 | 15 | /** 16 | * 教师表,用来测试接口是否正常 17 | * 对于校验注解,没有指定groups属性的,则默认是Default.class组, 18 | * 控制器里在save方法是使用默认组, 19 | * updateById,使用Update.class组,更新时允许部分字段为空,但是若有值则校验属性值的合法性 20 | */ 21 | @Setter 22 | @Getter 23 | @Entity 24 | public class Teacher extends BaseEntity { 25 | 26 | @Id 27 | @GeneratedValue(strategy = GenerationType.IDENTITY) 28 | @NotNull(message = "id {property.not.allow.empty}", groups = Update.class) // 意味着,updateById更新时id不允许为空 29 | private Long id; 30 | 31 | // 不论新增和更新都应符合要求 32 | @Length(min = 2, max = 32, message = "{Student.name} {property.length.in.between}", groups = {Update.class, Default.class}) 33 | // 意思是,新增时不允许为空,updateById更新时可为空! 34 | @NotBlank 35 | private String name; 36 | 37 | @PositiveOrZero(groups = {Update.class, Default.class}) 38 | private Integer age; 39 | 40 | @Email(groups = {Update.class, Default.class}) 41 | private String email; 42 | 43 | @Past(groups = {Update.class, Default.class}) 44 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 45 | @Temporal(TemporalType.DATE) 46 | private Date birthday; 47 | 48 | @OneToOne(targetEntity = Course.class) 49 | @JoinColumn(name = "course_id", referencedColumnName = "id", unique = true) 50 | private Course course; // 一个老师关联一个们学科 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/repository/CourseRepository.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.repository; 2 | 3 | import vip.efactory.ejpa.base.repository.BaseRepository; 4 | import vip.efactory.ejpa.example.entity.Course; 5 | 6 | /** 7 | * 课程持久层 8 | * @author dbdu 9 | */ 10 | public interface CourseRepository extends BaseRepository { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/repository/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.repository; 2 | 3 | import vip.efactory.ejpa.base.repository.BaseRepository; 4 | import vip.efactory.ejpa.example.entity.Student; 5 | 6 | public interface StudentRepository extends BaseRepository { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/repository/SysTenantRepository.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.repository; 2 | 3 | import vip.efactory.ejpa.base.repository.BaseRepository; 4 | import vip.efactory.ejpa.example.entity.SysTenant; 5 | 6 | public interface SysTenantRepository extends BaseRepository { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/repository/TeacherRepository.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.repository; 2 | 3 | import vip.efactory.ejpa.base.repository.BaseRepository; 4 | import vip.efactory.ejpa.example.entity.Teacher; 5 | 6 | /** 7 | * 教师持久层 8 | * @author dbdu 9 | */ 10 | public interface TeacherRepository extends BaseRepository { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/ICourseService.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service; 2 | 3 | import vip.efactory.ejpa.base.service.IBaseService; 4 | import vip.efactory.ejpa.example.entity.Course; 5 | 6 | /** 7 | * @author dbdu 8 | */ 9 | public interface ICourseService extends IBaseService { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/IStudentService.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service; 2 | 3 | import vip.efactory.ejpa.base.service.IBaseService; 4 | import vip.efactory.ejpa.example.entity.Student; 5 | 6 | public interface IStudentService extends IBaseService { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/ISysTenantService.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service; 2 | 3 | import vip.efactory.ejpa.base.service.IBaseService; 4 | import vip.efactory.ejpa.example.entity.SysTenant; 5 | 6 | public interface ISysTenantService extends IBaseService { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/ITeacherService.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service; 2 | 3 | import vip.efactory.ejpa.base.service.IBaseService; 4 | import vip.efactory.ejpa.example.entity.Teacher; 5 | 6 | /** 7 | * @author dbdu 8 | */ 9 | public interface ITeacherService extends IBaseService { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/impl/CourseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service.impl; 2 | 3 | import org.springframework.cache.annotation.CacheEvict; 4 | import org.springframework.cache.annotation.Cacheable; 5 | import org.springframework.data.domain.Example; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | import org.springframework.data.domain.Sort; 9 | import org.springframework.stereotype.Service; 10 | import vip.efactory.common.base.bean.ObserveData; 11 | import vip.efactory.common.base.bean.OperateTypeEnum; 12 | import vip.efactory.ejpa.base.service.impl.BaseServiceImpl; 13 | import vip.efactory.ejpa.example.entity.Course; 14 | import vip.efactory.ejpa.example.repository.CourseRepository; 15 | import vip.efactory.ejpa.example.service.ICourseService; 16 | 17 | import java.util.List; 18 | import java.util.Optional; 19 | import java.util.Set; 20 | 21 | /** 22 | * @author dbdu 23 | */ 24 | @Service 25 | public class CourseServiceImpl extends BaseServiceImpl implements ICourseService { 26 | 27 | // 以下这些方法都是不变更数据的查询方法,若为了提高并发性能,可以尽可能的加持缓存 ----开始 28 | @Override 29 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 30 | public Iterable findAll() { 31 | return super.findAll(); 32 | } 33 | 34 | @Override 35 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 36 | public Page findAll(Pageable var1) { 37 | return super.findAll(var1); 38 | } 39 | 40 | @Override 41 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 42 | public Iterable findAll(Sort var1) { 43 | return super.findAll(var1); 44 | } 45 | 46 | @Override 47 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 48 | public Iterable findAllById(Iterable var1) { 49 | return super.findAllById(var1); 50 | } 51 | 52 | @Override 53 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 54 | public Course getOne(Long aLong) { 55 | return super.getOne(aLong); 56 | } 57 | 58 | @Override 59 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 60 | public Optional findById(Long var1) { 61 | return super.findById(var1); 62 | } 63 | 64 | @Override 65 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 66 | public Optional findOne(Example example) { 67 | return super.findOne(example); 68 | } 69 | 70 | @Override 71 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 72 | public List findAll(Example var1) { 73 | return super.findAll(var1); 74 | } 75 | 76 | @Override 77 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 78 | public List findAll(Example var1, Sort var2) { 79 | return super.findAll(var1, var2); 80 | } 81 | 82 | @Override 83 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 84 | public Page findAll(Example example, Pageable pageable) { 85 | return super.findAll(example, pageable); 86 | } 87 | 88 | @Override 89 | public void flush() { 90 | super.flush(); 91 | } 92 | 93 | @Override 94 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 95 | public boolean existsById(Long var1) { 96 | return super.existsById(var1); 97 | } 98 | 99 | @Override 100 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 101 | public long count() { 102 | return super.count(); 103 | } 104 | 105 | @Override 106 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 107 | public long count(Example example) { 108 | return super.count(example); 109 | } 110 | 111 | @Override 112 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 113 | public boolean exists(Example example) { 114 | return super.exists(example); 115 | } 116 | 117 | @Override 118 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 119 | public boolean existsByEntityProperty(String propertyName, String propertyValue) throws NoSuchFieldException { 120 | return super.existsByEntityProperty(propertyName, propertyValue); 121 | } 122 | 123 | @Override 124 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 125 | public List advancedQuery(Course entity) { 126 | return super.advancedQuery(entity); 127 | } 128 | 129 | @Override 130 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 131 | public Page advancedQuery(Course entity, Pageable pageable) { 132 | return super.advancedQuery(entity, pageable); 133 | } 134 | 135 | @Override 136 | @Cacheable(value = "COURSE_DETAILS", unless = "#result == null") 137 | public Set advanceSearchProperty(String property, String value) { 138 | return super.advanceSearchProperty(property, value); 139 | } 140 | // 以上这些方法都是不变更数据的查询方法,若为了提高并发性能,可以尽可能的加持缓存 ----结束 141 | 142 | 143 | // 以下是涉及到数据库变更的所有方法开始,这些方法不仅要维护自身的缓存,还要通知哪些依赖并观察自己的观察者去更新他们的缓存 144 | @Override 145 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 146 | public Iterable saveAll(Iterable var1) { 147 | return super.saveAll(var1); 148 | } 149 | 150 | @Override 151 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 152 | public S saveAndFlush(S var1) { 153 | return super.saveAndFlush(var1); 154 | } 155 | 156 | @Override 157 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 158 | public S save(S var1) { 159 | return super.save(var1); 160 | } 161 | 162 | @Override 163 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 164 | public void delete(Course var1) { 165 | super.delete(var1); 166 | } 167 | 168 | @Override 169 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 170 | public void deleteById(Long var1) { 171 | super.deleteById(var1); 172 | } 173 | 174 | @Override 175 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 176 | public void deleteAll() { 177 | super.deleteAll(); 178 | } 179 | 180 | @Override 181 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 182 | public void deleteAll(Iterable var1) { 183 | super.deleteAll(var1); 184 | } 185 | 186 | @Override 187 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 188 | public void deleteAllInBatch() { 189 | super.deleteAllInBatch(); 190 | } 191 | 192 | @Override 193 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 194 | public void deleteInBatch(Iterable var1) { 195 | super.deleteInBatch(var1); 196 | } 197 | 198 | @Override 199 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 200 | public int deleteAllById(Iterable var1) { 201 | return super.deleteAllById(var1); 202 | } 203 | 204 | @Override 205 | @CacheEvict(value = "COURSE_DETAILS", allEntries = true) 206 | public S update(S var1) { 207 | super.update(var1); 208 | // 构造变更的详细信息,以便观察者可以精细地更新自己的缓存 209 | ObserveData arg = new ObserveData(this.getClass().getSimpleName(), OperateTypeEnum.UPDATE.getCode(), var1.getId().toString()); 210 | // 通知观察者进行更新 211 | notifyOthers(arg); 212 | return var1; 213 | } 214 | // 以上是涉及到数据库变更的所有方法 --------结束 215 | 216 | // 以下这些方法是处理关联缓存一致性的 不过因为本组件仅被别人依赖而不依赖别人,所以只需要被观察不需要观察别人 ----开始 217 | 218 | @Override 219 | public void notifyOthers(Object arg) { 220 | super.notifyOthers(arg); 221 | } 222 | 223 | // 以下这些方法是处理关联缓存一致性的 不过因为本组件仅被别人依赖而不依赖别人,所以只需要被观察不需要观察别人 ----结束 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/impl/StudentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import vip.efactory.ejpa.base.service.impl.BaseServiceImpl; 5 | import vip.efactory.ejpa.example.entity.Student; 6 | import vip.efactory.ejpa.example.repository.StudentRepository; 7 | import vip.efactory.ejpa.example.service.IStudentService; 8 | 9 | @Service 10 | public class StudentServiceImpl extends BaseServiceImpl implements IStudentService { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/impl/SysTenantServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import vip.efactory.ejpa.base.service.impl.BaseServiceImpl; 5 | import vip.efactory.ejpa.example.entity.SysTenant; 6 | import vip.efactory.ejpa.example.repository.SysTenantRepository; 7 | import vip.efactory.ejpa.example.service.ISysTenantService; 8 | 9 | @Service 10 | public class SysTenantServiceImpl extends BaseServiceImpl implements ISysTenantService { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vip/efactory/ejpa/example/service/impl/TeacherServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example.service.impl; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.cache.annotation.CacheEvict; 5 | import org.springframework.cache.annotation.Cacheable; 6 | import org.springframework.data.domain.Example; 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.data.domain.Pageable; 9 | import org.springframework.data.domain.Sort; 10 | import org.springframework.stereotype.Service; 11 | import vip.efactory.ejpa.base.service.impl.BaseServiceImpl; 12 | import vip.efactory.ejpa.example.entity.Teacher; 13 | import vip.efactory.ejpa.example.repository.TeacherRepository; 14 | import vip.efactory.ejpa.example.service.ITeacherService; 15 | 16 | import java.util.List; 17 | import java.util.Observable; 18 | import java.util.Optional; 19 | import java.util.Set; 20 | 21 | /** 22 | * @author dbdu 23 | */ 24 | @Service 25 | @Slf4j 26 | public class TeacherServiceImpl extends BaseServiceImpl implements ITeacherService { 27 | // 以下这些方法都是不变更数据的查询方法,若为了提高并发性能,可以尽可能的加持缓存 ----开始 28 | 29 | @Override 30 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 31 | public Iterable findAll() { 32 | return super.findAll(); 33 | } 34 | 35 | @Override 36 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 37 | public Page findAll(Pageable var1) { 38 | return super.findAll(var1); 39 | } 40 | 41 | @Override 42 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 43 | public Iterable findAll(Sort var1) { 44 | return super.findAll(var1); 45 | } 46 | 47 | @Override 48 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 49 | public Iterable findAllById(Iterable var1) { 50 | return super.findAllById(var1); 51 | } 52 | 53 | @Override 54 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 55 | public Teacher getOne(Long aLong) { 56 | return super.getOne(aLong); 57 | } 58 | 59 | @Override 60 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 61 | public Optional findById(Long var1) { 62 | return super.findById(var1); 63 | } 64 | 65 | @Override 66 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 67 | public Optional findOne(Example example) { 68 | return super.findOne(example); 69 | } 70 | 71 | @Override 72 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 73 | public List findAll(Example var1) { 74 | return super.findAll(var1); 75 | } 76 | 77 | @Override 78 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 79 | public List findAll(Example var1, Sort var2) { 80 | return super.findAll(var1, var2); 81 | } 82 | 83 | @Override 84 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 85 | public Page findAll(Example example, Pageable pageable) { 86 | return super.findAll(example, pageable); 87 | } 88 | 89 | @Override 90 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 91 | public boolean existsById(Long var1) { 92 | return super.existsById(var1); 93 | } 94 | 95 | @Override 96 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 97 | public long count() { 98 | return super.count(); 99 | } 100 | 101 | @Override 102 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 103 | public long count(Example example) { 104 | return super.count(example); 105 | } 106 | 107 | @Override 108 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 109 | public boolean exists(Example example) { 110 | return super.exists(example); 111 | } 112 | 113 | @Override 114 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 115 | public boolean existsByEntityProperty(String propertyName, String propertyValue) throws NoSuchFieldException { 116 | return super.existsByEntityProperty(propertyName, propertyValue); 117 | } 118 | 119 | @Override 120 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 121 | public List advancedQuery(Teacher entity) { 122 | return super.advancedQuery(entity); 123 | } 124 | 125 | @Override 126 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 127 | public Page advancedQuery(Teacher entity, Pageable pageable) { 128 | return super.advancedQuery(entity, pageable); 129 | } 130 | 131 | @Override 132 | @Cacheable(value = "TEACHER_DETAILS", unless = "#result == null") 133 | public Set advanceSearchProperty(String property, String value) { 134 | return super.advanceSearchProperty(property, value); 135 | } 136 | // 以下这些方法都是不变更数据的查询方法,若为了提高并发性能,可以尽可能的加持缓存 ----结束 137 | 138 | // 以下是涉及到数据库变更的所有方法开始,这些方法不仅要维护自身的缓存,还要通知哪些依赖并观察自己的观察者去更新他们的缓存 139 | @Override 140 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 141 | public Iterable saveAll(Iterable var1) { 142 | return super.saveAll(var1); 143 | } 144 | 145 | @Override 146 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 147 | public S saveAndFlush(S var1) { 148 | return super.saveAndFlush(var1); 149 | } 150 | 151 | @Override 152 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 153 | public S save(S var1) { 154 | return super.save(var1); 155 | } 156 | 157 | @Override 158 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 159 | public void delete(Teacher var1) { 160 | super.delete(var1); 161 | } 162 | 163 | @Override 164 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 165 | public void deleteById(Long var1) { 166 | super.deleteById(var1); 167 | } 168 | 169 | @Override 170 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 171 | public void deleteAll() { 172 | super.deleteAll(); 173 | } 174 | 175 | @Override 176 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 177 | public void deleteAll(Iterable var1) { 178 | super.deleteAll(var1); 179 | } 180 | 181 | @Override 182 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 183 | public void deleteAllInBatch() { 184 | super.deleteAllInBatch(); 185 | } 186 | 187 | @Override 188 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 189 | public void deleteInBatch(Iterable var1) { 190 | super.deleteInBatch(var1); 191 | } 192 | 193 | @Override 194 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 195 | public int deleteAllById(Iterable var1) { 196 | return super.deleteAllById(var1); 197 | } 198 | 199 | @Override 200 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 201 | public S update(S var1) { 202 | return super.update(var1); 203 | } 204 | 205 | // 以上是涉及到数据库变更的所有方法 --------结束 206 | 207 | // 以下这些方法是处理关联缓存一致性的 此处模拟:Teacher只观察Course一个组件,当Course变更时就可以更新Teacher对应的缓存 ----开始 208 | 209 | // 联动清除本地的缓存! 210 | @Override 211 | @CacheEvict(value = "TEACHER_DETAILS", allEntries = true) 212 | public void update(Observable o, Object arg) { 213 | log.info("联动清除Teacher缓存信息....."); 214 | log.info("Teacer组件收到的数据是:" + arg); 215 | log.info("如果不是全部清空Teacher的缓存数据,可以利用收到的数据进行精细化更新自己的缓存!"); 216 | } 217 | 218 | // 以下这些方法是处理关联缓存一致性的 此处模拟:Teacher只观察Course一个组件,当Course变更时就可以更新Teacher对应的缓存 ----结束 219 | 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | spring: 5 | jpa: 6 | database: mysql 7 | hibernate: 8 | # 就算此处是update,似乎也只是默认数据源会更新 9 | ddl-auto: update 10 | show-sql: true 11 | properties: 12 | hibernate.dialect: org.hibernate.dialect.MySQL8Dialect 13 | # redis 缓存 14 | redis: 15 | host: ${REDIS-HOST:localhost} 16 | port: 6379 17 | database: 3 18 | # 此数据源应包含多租户的数据源管理表,通过该表进行多租户数据源的初始化。 19 | datasource: 20 | driverClassName: com.mysql.cj.jdbc.Driver 21 | url: jdbc:mysql://localhost:3306/db_ejpa_example_1?characterEncoding=utf8&useSSL=false 22 | username: root 23 | password: 123456 24 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=创建时间 4 | BaseEntity.updateTime=最后更新时间 5 | BaseEntity.creatorNum=创建人 6 | BaseEntity.updaterNum=最后更新人 7 | BaseEntity.remark=备注 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=主键 11 | Student.name=姓名 12 | Student.sex=性别 13 | Student.age=年龄 14 | Student.hobby=爱好 15 | Student.height=身高 16 | Student.email=邮箱 17 | Student.birthday=生日 -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_de_DE.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_en_CA.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_en_GB.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_en_US.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_fr_CA.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_fr_FR.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_it_IT.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_ja_JP.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_ko_KR.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_ru_KZ.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_ru_RU.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=创建时间 4 | BaseEntity.updateTime=最后更新时间 5 | BaseEntity.creatorNum=创建人 6 | BaseEntity.updaterNum=最后更新人 7 | BaseEntity.remark=备注 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=主键 11 | Student.name=姓名 12 | Student.sex=性别 13 | Student.age=年龄 14 | Student.hobby=爱好 15 | Student.height=身高 16 | Student.email=邮箱 17 | Student.birthday=生日 18 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_zh_TW.properties: -------------------------------------------------------------------------------- 1 | # This file create at Sun Jan 12 09:38:53 CST 2020 ,by Entityi18nUtil! 2 | #vip.efactory.ejpa.base.entity.BaseEntity 3 | BaseEntity.createTime=createTime 4 | BaseEntity.updateTime=updateTime 5 | BaseEntity.creatorNum=creatorNum 6 | BaseEntity.updaterNum=updaterNum 7 | BaseEntity.remark=remark 8 | 9 | #vip.efactory.ejpa.example.entity.Student 10 | Student.id=id 11 | Student.name=name 12 | Student.sex=sex 13 | Student.age=age 14 | Student.hobby=hobby 15 | Student.height=height 16 | Student.email=email 17 | Student.birthday=birthday 18 | -------------------------------------------------------------------------------- /src/main/resources/sql/MutiTenantTest.sql: -------------------------------------------------------------------------------- 1 | -- -------------------------------------------------------- 2 | -- 主机: 127.0.0.1 3 | -- 服务器版本: 8.0.17 - MySQL Community Server - GPL 4 | -- 服务器操作系统: Linux 5 | -- HeidiSQL 版本: 10.3.0.5771 6 | -- -------------------------------------------------------- 7 | 8 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 9 | /*!40101 SET NAMES utf8 */; 10 | /*!50503 SET NAMES utf8mb4 */; 11 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 12 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 13 | 14 | -- 创建测试的数据库 15 | CREATE DATABASE IF NOT EXISTS `db_ejpa_example_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_zh_0900_as_cs */ /*!80016 DEFAULT ENCRYPTION='N' */; 16 | CREATE DATABASE IF NOT EXISTS `db_ejpa_example_2` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_zh_0900_as_cs */ /*!80016 DEFAULT ENCRYPTION='N' */; 17 | CREATE DATABASE IF NOT EXISTS `db_ejpa_example_3` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_zh_0900_as_cs */ /*!80016 DEFAULT ENCRYPTION='N' */; 18 | -- 使用默认库 19 | USE `db_ejpa_example_1`; 20 | 21 | -- 创建系统租户表 22 | CREATE TABLE IF NOT EXISTS `sys_tenant` ( 23 | `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 24 | `tenant_name` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '租户名称', 25 | `tenant_code` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '租户编码', 26 | `db_username` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT 'DB用户名', 27 | `db_password` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT 'DB密码', 28 | `driver_class_name` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '驱动URL', 29 | `jdbc_url` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '租户名称', 30 | `remark` varchar(1024) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '备注', 31 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 32 | `creator_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '创建人', 33 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 34 | `updater_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '更新人', 35 | PRIMARY KEY (`id`) 36 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_zh_0900_as_cs; 37 | 38 | -- 创建学生表 39 | CREATE TABLE IF NOT EXISTS `student` ( 40 | `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 41 | `name` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '姓名', 42 | `age` INT(11) NOT NULL COMMENT '年龄', 43 | `birthday` DATE NULL DEFAULT NULL COMMENT '生日', 44 | `sex` INT(11) NOT NULL COMMENT '性别', 45 | `email` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '邮件', 46 | `hobby` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '爱好', 47 | `height` DOUBLE NULL DEFAULT NULL COMMENT '身高', 48 | `remark` varchar(1024) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '备注', 49 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 50 | `creator_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '创建人', 51 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 52 | `updater_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '更新人', 53 | PRIMARY KEY (`id`) 54 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_zh_0900_as_cs; 55 | 56 | -- 插入租户测试数据 57 | INSERT INTO `sys_tenant` (`id`,`db_password`, `db_username`, `driver_class_name`, `jdbc_url`, `tenant_code`, `tenant_name`) VALUES 58 | (1,'123456', 'root', 'com.mysql.cj.jdbc.Driver', 'jdbc:mysql://localhost:3306/db_ejpa_example_1?characterEncoding=utf8&useSSL=false', 'DEFAULT', '默认租户1'), 59 | (2,'123456', 'root', 'com.mysql.cj.jdbc.Driver', 'jdbc:mysql://localhost:3306/db_ejpa_example_2?characterEncoding=utf8&useSSL=false', 'DEFAULT', '租户2'), 60 | (3,'123456', 'root', 'com.mysql.cj.jdbc.Driver', 'jdbc:mysql://localhost:3306/db_ejpa_example_3?characterEncoding=utf8&useSSL=false', 'DEFAULT', '租户3'); 61 | 62 | -- 使用租户2 63 | USE `db_ejpa_example_2`; 64 | -- 创建学生表,表定义和上面是一样的 65 | CREATE TABLE IF NOT EXISTS `student` ( 66 | `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 67 | `name` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '姓名', 68 | `age` INT(11) NOT NULL COMMENT '年龄', 69 | `birthday` DATE NULL DEFAULT NULL COMMENT '生日', 70 | `sex` INT(11) NOT NULL COMMENT '性别', 71 | `email` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '邮件', 72 | `hobby` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '爱好', 73 | `height` DOUBLE NULL DEFAULT NULL COMMENT '身高', 74 | `remark` varchar(1024) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '备注', 75 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 76 | `creator_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '创建人', 77 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 78 | `updater_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '更新人', 79 | PRIMARY KEY (`id`) 80 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_zh_0900_as_cs; 81 | 82 | -- 使用租户3 83 | USE `db_ejpa_example_3`; 84 | -- 创建学生表,表定义和上面是一样的 85 | CREATE TABLE IF NOT EXISTS `student` ( 86 | `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 87 | `name` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '姓名', 88 | `age` INT(11) NOT NULL COMMENT '年龄', 89 | `birthday` DATE NULL DEFAULT NULL COMMENT '生日', 90 | `sex` INT(11) NOT NULL COMMENT '性别', 91 | `email` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '邮件', 92 | `hobby` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_zh_0900_as_cs' COMMENT '爱好', 93 | `height` DOUBLE NULL DEFAULT NULL COMMENT '身高', 94 | `remark` varchar(1024) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '备注', 95 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 96 | `creator_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '创建人', 97 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 98 | `updater_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '更新人', 99 | PRIMARY KEY (`id`) 100 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_zh_0900_as_cs; 101 | 102 | /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; 103 | /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; 104 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 105 | -------------------------------------------------------------------------------- /src/main/resources/sql/student_test_data.sql: -------------------------------------------------------------------------------- 1 | -- -------------------------------------------------------- 2 | -- 主机: 127.0.0.1 3 | -- 服务器版本: 8.0.17 - MySQL Community Server - GPL 4 | -- 服务器操作系统: Linux 5 | -- HeidiSQL 版本: 10.3.0.5771 6 | -- -------------------------------------------------------- 7 | 8 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 9 | /*!40101 SET NAMES utf8 */; 10 | /*!50503 SET NAMES utf8mb4 */; 11 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 12 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 13 | 14 | 15 | -- 导出 db_ejpa_example 的数据库结构 16 | CREATE DATABASE IF NOT EXISTS `db_ejpa_example` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_zh_0900_as_cs */ /*!80016 DEFAULT ENCRYPTION='N' */; 17 | USE `db_ejpa_example`; 18 | 19 | -- 导出 表 db_ejpa_example.student 结构 20 | CREATE TABLE IF NOT EXISTS `student` ( 21 | `id` bigint(20) NOT NULL, 22 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '????', 23 | `creator_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '???', 24 | `remark` varchar(1024) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '??', 25 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '????', 26 | `updater_num` varchar(32) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL COMMENT '???', 27 | `age` int(11) NOT NULL, 28 | `birthday` date DEFAULT NULL, 29 | `name` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL, 30 | `sex` int(11) NOT NULL, 31 | `email` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL, 32 | `hobby` varchar(255) COLLATE utf8mb4_zh_0900_as_cs DEFAULT NULL, 33 | `height` double DEFAULT NULL, 34 | PRIMARY KEY (`id`) 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_zh_0900_as_cs; 36 | 37 | -- 正在导出表 db_ejpa_example.student 的数据:~4 rows (大约) 38 | DELETE FROM `student`; 39 | /*!40000 ALTER TABLE `student` DISABLE KEYS */; 40 | INSERT INTO `student` (`id`, `create_time`, `creator_num`, `remark`, `update_time`, `updater_num`, `age`, `birthday`, `name`, `sex`, `email`, `hobby`, `height`) VALUES 41 | (1, '2019-12-09 14:49:04', NULL, NULL, '2019-12-18 17:30:52', NULL, 34, '1984-10-15', 'joy chuo', 1, NULL, '篮球', 150), 42 | (2, '2019-12-09 14:49:59', NULL, NULL, '2019-12-18 17:30:57', NULL, 45, '1978-10-15', 'tim chuo', 1, NULL, '足球', 165), 43 | (3, '2019-12-09 14:50:39', NULL, NULL, '2019-12-18 17:31:03', NULL, 30, '1978-07-15', 'jimmy blue', 1, NULL, '乒乓球', 170), 44 | (4, '2019-12-09 14:51:00', NULL, NULL, '2019-12-18 17:31:09', NULL, 30, '1978-07-15', 'cammny white', 1, NULL, '台球', 180); 45 | /*!40000 ALTER TABLE `student` ENABLE KEYS */; 46 | 47 | /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; 48 | /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; 49 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 50 | -------------------------------------------------------------------------------- /src/main/resources/static/payimg/ali-pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/payimg/ali-pay.png -------------------------------------------------------------------------------- /src/main/resources/static/payimg/wx-pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/payimg/wx-pay.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/1-ISNULL-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/1-ISNULL-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/1-NOTNULL-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/1-NOTNULL-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/2-Fuzzy-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/2-Fuzzy-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/2-LeftFuzzy-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/2-LeftFuzzy-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/2-RightFuzzy-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/2-RightFuzzy-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/3-EqualQuery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/3-EqualQuery.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/3-NotEqualQuery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/3-NotEqualQuery.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/4-BetweenQuery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/4-BetweenQuery.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/5-GE-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/5-GE-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/5-GT-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/5-GT-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/5-LE-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/5-LE-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/单条件查询/5-LT-Query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/单条件查询/5-LT-Query.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/多条件不分组查询/1-多条件AND查询.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/多条件不分组查询/1-多条件AND查询.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/多条件分组查询/1-多条件分组查询.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/多条件分组查询/1-多条件分组查询.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/advQuery/数据记录.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/advQuery/数据记录.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/check/实体属性约束检查.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/check/实体属性约束检查.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/AdvanceQuery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/AdvanceQuery.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/DeleteByID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/DeleteByID.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/GetById.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/GetById.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/Page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/Page.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/Save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/Save.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/Update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/Update.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/crud/fuzzy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/crud/fuzzy.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/i18n/1-在头中带英文的国际化参数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/i18n/1-在头中带英文的国际化参数.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/i18n/3-在请求地址中带英文的国际化参数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/i18n/3-在请求地址中带英文的国际化参数.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/i18n/0-不带国际化参数,默认中文简体.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/i18n/0-不带国际化参数,默认中文简体.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/i18n/2-在头中带中文的国际化参数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/i18n/2-在头中带中文的国际化参数.png -------------------------------------------------------------------------------- /src/main/resources/static/usage/i18n/4-在请求地址中带中文的国际化参数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vip-efactory/ejpa-example/7ea994513b5fba755cd2e667183c8a00316b4a38/src/main/resources/static/usage/i18n/4-在请求地址中带中文的国际化参数.png -------------------------------------------------------------------------------- /src/test/java/vip/efactory/ejpa/example/EjpaExampleApplicationTests.java: -------------------------------------------------------------------------------- 1 | package vip.efactory.ejpa.example; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EjpaExampleApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------