饭饭's Blog - Java
type: channel
link: https://ffis.me/Java/
description: Java学习心得分享
items: 使用雪花算法生成16位全局唯一自增ID
items: 使用HotSwapAgent实现SpringBoot热加载
items: Drone下多工程项目使用Commit日志控制子工程运行
items: 使用docker来构建一个SpringBoot应用
items: SpringBoot 配置文件存放位置及读取顺序
items: 解决Spring项目打成Jar包后Freemarker找不到模板的问题
items: 记一次解决RestTemplate无法解析api返回的xml数据问题
items: Git分布式版本控制工具
items: mybatis03 - 多表查询、简单的SSM整合
items: mybatis02 - 核心配置文件深入

使用雪花算法生成16位全局唯一自增ID
type: item
date: 2023-09-16T18:03:00+08:00
description: 背景实际业务中，往往会用数据库自增ID来作为业务对象的唯一ID；但数据日渐增多的互联网行业，分库分表则成了行业的通用解决方案；此时数据库自增ID就不能满足业务需求了，行业内有各种分布式ID生成解决方案，其中雪花ID就是其中使用较多的一种。雪花算法原理1 基本构成雪花算法（Snowflake）是 Twitter 开源的分布式 ID 生成算法，可以基于时间生成全局不重复的、有序的、可自增的 64 Bit 的 ID，适用于分布式系统中的 ID 生成需求。在标准版本中由以下部分组成：符号位（1bit）- 时间戳相对值（41bit）- 数据标志（5bit）- 机器标志（5bit）- 递增序号（12bit）0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000一起来一共64Bit组成，其中：1、最高位是符号位，用于区分是正数还是负数，这里始终为0，我们用不到；2、41位的毫秒级时间戳，41位的长度可以使用69年；3、5位datacenterId和5位workerId，加起来共10位，最多支持32 x 32 = 1024个节点4、最后12位是毫秒内的计数，12位的计数顺序号支持每个节点每毫秒产生4096个ID序号2 生成ID原理标准版雪花算法在实例化时，由于支持的时间范围有限制，所以开始的时候需要指定一个开始时间，并指定数据标识和机器标识；如设定开始时间为 2020-01-01 00:00:00，数据标识设置为：15，机器标识设置为18；此时我们想要生成1个雪花ID1、获取时间戳差值先获取当前时间戳与设定的开始时间的差值当前时间2023-09-16 17:00:00转换为时间戳：1694854800000开始时间2020-01-01 00:00:00转换为时间戳：1577808000000计算时间戳差值：1694854800000 - 1577808000000 = 117046800000转换为二级制：11011010000001000100001101110100000002、获取数据标识和机器标识数据标识：15；机器标识：18分别转换为二级制：数据标识：1111；机器标识：100103、获取毫秒内计数毫秒内计数即是同一毫秒内，获取的第n个雪花ID，假设我们获取的是第1024个雪花ID1024换算为二级制：10000000000则生成的雪花ID为：时间戳差值+数据标识+机器标识+毫秒内计数1101101000000100010000110111010000000 1111 10010 10000000000二进制转为十进制，生成的雪花ID即为：122732465357820928 - 共18位雪花算法优缺点1 优点唯一性：雪花算法生成的ID是全局唯一的，可以在分布式系统中生成不重复的业务ID；有序性：基于时间戳的特性，使得雪花算法生成的ID是顺序增长的；高性能：雪花算法的生成过程是基于位运算实现的，性能好，并且标准的雪花算法每毫秒支持4096个ID生成，满足绝大多数的业务场景；独立性：雪花算法不依赖中央系统或数据库，非常方便在业务中落地和进行横向水平扩展；2 缺点依赖系统时钟：雪花算法依赖系统时钟，如果时钟发生回退，会导致ID生成重复，当然这个都有对应的解决方案；有限的容量：标准的雪花算法支持每毫秒生成4096个ID，如果超过了容量限制，则需要等待下一毫秒才可以生成新的ID；前端精度丢失：标准雪花算法生成的ID在18位和19位之间（时间差超过7.56年，就会达到19位），JavaScript 的 number 类型的数值范围是：2的53次方减1，所以数字位数大于16位且大于 9007199254740991 的数，进制转换会存在精度问题，而雪花ID生成的数值过大，导致超出JavaScript的精度范围，无法直接在前端展示，使用字符串展示雪花ID可避免此问题。定制16位雪花ID1 雪花算法定制既然JavaScript只支持16位一下的数字展示，那我们生成16位以下的雪花ID即可；标准的雪花算法为64Bit，每个位置的设计冗余量都很大，业务中完全可以根据自己的业务形态去定制雪花算法；1、缩短支持时间标准雪花算法为41位时间戳差值，支持使用69年，实际业务中很少有能跑69年的业务，我们可以将时间戳差值修改为39位，39位时间戳支持使用：(2^39)/(1000606024365) = 17.43 年，17年足够大部分业务使用；2、只保留机器标识标准雪花算法支持：5位的数据标识和5位的机器标识即最大支持 32 x 32 = 1024 节点部署实际业务很少有这么大规模的机器部署，一般最多也就10台机器，这里完全可以将5位数据标识去除，只保留5…

使用HotSwapAgent实现SpringBoot热加载
type: item
date: 2022-02-24T22:32:00+08:00
description: 众所周知，IDEA 自带的热加载只支持方法内的热加载，而使用 HotSwapAgent 不仅支持方法内的热加载，并且可实现新增方法的热加载，甚至是新增类的热加载，可谓是提高开发效率的神器 本文主要介绍在 IDEA 下使用 HotSwapAgent 来进行 SpringBoot 下的热部署；接下来我们开始配置 HotSwapAgent1 安装 DCEVMDCEVM 是个JDK的插件，提供类似 JRebel 的热加载功能，能够在运行时重新定义加载的类，实现“热加载、热插拔、热部署”，而 HotSwapAgent 插件则是实现了 Servlet 程序的热加载功能，并且 DCEVM + HotSwapAgent 开源免费，更适合广大开发者使用。安装 DCEVM 需要和 JDK 版本相对应，你需要先确定自己的 JDK 版本是否是 DCEVM 所支持的版本https://github.com/dcevm/dcevm/releases写此文时，DCEVM 支持的最新 JDK 版本是Java 8u181如果你的JDK版本不被支持，则需要去 Oracle Java Archive 这个页面下载对应版本 JDK 并安装https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html下载 JDK 需要自备 Oracle 的账号下载并安装好对应版本 JDK 后，我们再次来确定 JDK 版本为对应版本此时我们可以下载 DCEVM patch 进行安装，这里我下载的是 DCEVM-8u181-installer-build2.jarhttps://github.com/dcevm/dcevm/releases运行以下命令开始进行安装java -jar DCEVM-8u181-installer-build2.jar这里需要选择安装目录，安装目录默认为 JDK 安装目录，如果没有则需要手动添加，然后点击 Install DCEVM as altjvm 这个按钮安装。2 IntelliJ IDEA 配置在 IDEA 插件库中搜索 HotSwapAgent 进行安装插件然后进行 HotSwapAgent plugin 配置勾选为所有项目启动 HotSwapAgent 插件PS：请手动检查插件目录下的 jar 包是否自动下载成功，如果下载不成功，则手动下载 jar 包替换图中的 Agent installation 路径为Jar包保存路径jar 包下载地址：https://github.com/HotswapProjects/HotswapAgent/releases3 热部署启动在 DEBUG 模式下启动应用程序。如果设置正确完成，您将在控制台中看到 HOTSWAP 代理通知。4 手动触发热部署方式在应用启动的过程中，如果有修改代码，可以手动触发热部署使其生效MacOS: Command+Shift+F9 / Win: Ctrl+Shift+F9手动触发热加载的时候，光标需要将焦点保持在需要热加载的类上面当然你也可以配置自动触发热部署，由于我不喜欢自动触发，所以在此不做过多介绍。5 热加载测试我们使用Spring Initializr快速创建一个 SpringBoot 应用，并且编写一个简单的测试接口：应用使用 DEBUG 模式启动的时候有提示被 HotSwapAgent 代理使用浏览器访问测试接口，正常访问1 修改方法体内代码，手动触发热加载提示1个类被重新加载再次访问接口，方法体内修改的代码已经生效2 新增一个方法，手动触发热加载提示1个类被重新加载，并且类被重新注册访问新增的接口，新增的方法已经生效3 新增一个类，手动触发热加载提示0个类被重新加载，但是新增的Controller2类已经被注册，说明只能重新加载被 Spring 注解标注的类访问新增的类接口，新增的类已经生效6 测试总结经测试，修改方法体内代码，修改方法，并且新增类都是可以支持热加载，但是新增类只支持注解扫描的类，XML配置文件不能进行热加载7 参考文档https://github.com/HotswapProjects/HotswapAgenthttps://github.com/dmitry-zhuravlev/hotswap-agent-intellij-plugin

Drone下多工程项目使用Commit日志控制子工程运行
type: item
date: 2021-08-11T23:36:00+08:00
description: 最近拖延症犯了，一个文章标题写了一个月才写了个标题... 前言自从入坑Drone CI/DI以来，我极力推荐在小微项目上使用Drone来完成自动构建，主要是轻量化，安装配置方便 ,只需写一个docker compose文件即可完成Drone的安装配置，只需写一个.drone.yml即可完成接入，极为方便；Drone更深一层探究经过我的不懈努力，经主管同意，最终也在公司项目上使用上了Drone来逐步替代Jenkins进行小项目微服务的自动构建部署 ；但是实际使用中发现一个问题，即SpringBoot工程，通常是一个主工程下包含多个微服务子工程，使用drone不太好控制其中某一个工程的自动构建部署，总不能每次都重新构建整个服务，然后重启所有工程吧，这样效率也太低了 ；//多工程目录结构，本文主要演示SpringBoot多工程项目自动构建部署 demoParant ├── common //公共工程 ├── api //API工程 ├── user //用户工程 └── back //后台工程能想到的最简单实现的方式就是来通过不同的分支来触发不同的构建任务，这样理论上可行，但是实际操作会产生一堆分支，显得极为不整洁，并且正常开发也是开发一个分支，测试一个分支，生产一个分支，太多了操作起来也不方便 ；所以最理想的方式也就是在同一个分支下，通过某种方式来触发不同的构建任务；Drone的启发在Drone CI下有一个默认功能，即在Commit log中输入[CI SKIP]即可跳过本次自动构建，于是我就想能否通过Commit log来控制本次部署具体哪个子工程，这样下来我只要在输入commit log的时候输入需要构建的工程，即可完成对应工程的自动构建部署，并且不影响同项目下的其他工程；初步设想的原理就是在drone执行部署命令时，通过自定义脚本完成工程部署，并将commit log作为参数传入脚本，在脚本中判断commit log中是否指定某些工程的运行的参数，如果不指定则默认运行所有工程 ；例如：我提交commit日志update Admin.java; add admin management interface; [CI API] [CI BACK];这样一来，经过drone自动构建后，只重新部署了API工程和后台工程；具体实现具体实现可参考如下脚本：Drone自动构建脚本参考# drone 自动构建 name: test-project autodeploy kind: pipeline type: docker # drone 构建步骤 steps: # 1. 进行 maven 打包 - name: maven compile pull: if-not-exists image: maven:ibmjava-alpine volumes: # 本机准备挂载到宿主机的 maven构建缓存 - name: cache path: /root/.m2 commands: # 开始打包maven工程 跳过测试步骤 - mvn clean install -Dmaven.test.skip=true - cp api/target/*.jar ./ - cp user/target/*.jar ./ - cp back/target/*.jar ./ # 2. 将打包后的jar包部署到指定服务器 - name: jar deploy pull: if-not-exists image: appleboy/drone-scp settings: host: from_secret: test_host username: from_secret: test_username password: from_secret: test_password port: from_secret: test_port target: /home/drone/data/${DRONE_REPO_NAME} source: ./*.jar when: branch: - master #3. 使用ssh访问测试服务器进行服务部署 - name: test ssh-start pull: if-not-exists image: appleboy/drone-ssh settings: host: from_secret: test_host username: from_secret: test_username password: from_secret: test_password port: from_secret:test_port script: # 运行部署脚本 - cd /data/testProjrct # 这一步是运行部署脚本，并将Com…

使用docker来构建一个SpringBoot应用
type: item
date: 2020-05-08T23:23:00+08:00
description: Dcoker是一个开源的应用容器引擎，介绍我就不介绍了，总之就是很牛批，用过的都相见恨晚 平时我们不是应用都是先搭建相关环境，然后把应用启动一通配置好本地环境需要搭建一次，测试环境需要搭建一次，生产环境也需要搭建一次，这样重复的工作就做了几次，效率降低 使用了docker了？我们只需要构建一次镜像，然后不同的环境只需要把镜像拉下来启动就行了，是不是很简单？如果配合CI/DI持续集成，持续部署，那就太爽歪歪了，我们只需要把代码提交到git，剩下打包/编译/测试/部署工作就全自动自己运行了，是不是大大提高了生产力？ 废话不多说，今天先研究docker怎么构建SpringBoot，改天再研究CI/DI~1 安装Docker首先docker只支持CentOS7以上的系统，如果系统版本过低请升级系统这里我使用的是CentOS7 x64docker安装教程详见：Docker的基本操作与使用2 准备SpringBoot应用这里我们使用idea来快速构建一个SpringBoot应用简单编写一个HelloController@RestController public class HelloController { @RequestMapping("/hello") public Object hello() { return "hello World!!!"; } }右键启动类运行，浏览器输入 http://localhost:8080/hello 可以看到启动成功3 创建Dockerfile构建文件我们创建docker文件夹，并在里面创建Dockerfile构建文件$ mkdir docker $ cd docker $ vim Dockerfile编写Dockerfile文件# Docker image for springboot file run # VERSION 0.0.1 # Author: Noisky # 基础镜像使用openjdk:8 FROM openjdk:8u181-jdk-alpine # 作者 MAINTAINER Noisky <noisky@ffis.me> # VOLUME 指定了临时文件目录为/tmp。 # 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件，并链接到容器的/tmp VOLUME /tmp # 将jar包添加到容器中并更名为app.jar ADD demo-0.0.1-SNAPSHOT.jar app.jar # 运行参数 ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]4 制作SpringBoot镜像将打包好的jar包放入docker文件夹，使docker文件夹只有Dockerfile和需要打包的jar文件docker安装并启动后，我们来制作SpringBoot镜像cd 进入docker目录运行以下命令来制作镜像，注意最后的.docker build -t springdemo:v1 . -t 参数是指定此镜像的名称如图所示开始制作，最后的.表示当前目录，也就是构建镜像的上下文路径。提示successfully表示构建成功这时候我们可以使用 docker images 来查看构建的镜像了5 从新镜像启动容器docker run -d -p 8188:8180 --name springdemo springdemo:v1 -d 参数是让容器后台运行 -p 是做端口映射，此时将服务器中的8188端口映射到容器中的8080(项目中端口配置的是8080)端口运行成功现在的SpringBoot应用是使用docker构建并管理运行的我们可以使用新端口来访问试试

SpringBoot 配置文件存放位置及读取顺序
type: item
date: 2020-04-02T22:17:00+08:00
description: 因为线上部署的时候和本地往往使用不同的配置文件，所以研究了下SpringBoot配置文件存放位置及读取顺序之前是直接手动指定配置文件启动来启动SpringBoot工程的，后来发现直接按照配置文件的加载顺序优先级放到对应的文件夹下即可进行配置文件的自动覆盖和互补以下内容转载自：https://my.oschina.net/sdlvzg/blog/1612703SpringBoot配置文件可以使用yml格式和properties格式分别的默认命名为：application.yml、application.properties存放目录SpringBoot配置文件默认可以放到以下目录中，可以自动读取到：项目根目录下项目根目录中 config 目录下项目的 resources 目录下项目 resources 目录中 config 目录下读取顺序如果在不同的目录中存在多个配置文件，它的读取顺序是：1、config/application.properties（项目根目录中config目录下）2、config/application.yml3、application.properties（项目根目录下）4、application.yml5、resources/config/application.properties（项目resources目录中config目录下）6、resources/config/application.yml7、resources/application.properties（项目的resources目录下）8、resources/application.yml注：1、如果同一个目录下，有application.yml也有application.properties，默认先读取application.properties。2、如果同一个配置属性，在多个配置文件都配置了，默认使用第1个读取到的，后面读取的不覆盖前面读取到的。3、创建SpringBoot项目时，一般的配置文件放置在“项目的resources目录下”

解决Spring项目打成Jar包后Freemarker找不到模板的问题
type: item
date: 2019-12-11T23:33:00+08:00
description: 昨天改完CheckDomain的bug后，打包jar准备部署到服务器上，本地测试都正常，但是扔到服务器上却发不了邮件了... 经过多次测试发现，freemarker在jar包中无法使用类加载器获取resourse目录下的templates文件 出现的问题代码如下：（本地测试正常，打包jar后无法获取模板）/** * 邮件模板静态化 * * @param mailTemplateModel 模板数据模型 * @return 加上数据后的静态化模板 */ @Override public String getMailHtml(MailTemplateModel mailTemplateModel) { try { //创建配置类 Configuration configuration = new Configuration(Configuration.getVersion()); //设置模板路径 String classpath = this.getClass().getResource("/").getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates")); //设置字符集 configuration.setDefaultEncoding("utf-8"); //加载模板 Template template = configuration.getTemplate("mailTemplate.ftl"); //模板静态化并返回 return FreeMarkerTemplateUtils.processTemplateIntoString(template, mailTemplateModel); } catch (Exception e) { e.printStackTrace(); log.error("模板静态化异常", e); return null; } }修改后的代码：（打包jar后正常获取模板）@Autowired FreeMarkerConfigurer freeMarkerConfigurer; /** * 邮件模板静态化 * * @param mailTemplateModel 模板数据模型 * @return 加上数据后的静态化模板 */ @Override public String getMailHtml(MailTemplateModel mailTemplateModel) { try { //创建配置类 Configuration configuration = new Configuration(Configuration.getVersion()); //设置模板路径 //String classpath = this.getClass().getResource("/").getPath(); //configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates")); //设置字符集 configuration.setDefaultEncoding("utf-8"); //加载模板 //Template template = configuration.getTemplate("mailTemplate.ftl"); Template template = freeMarkerConfigurer.getConfiguration().getTemplate("mailTemplate.ftl"); //模板静态化并返回 return FreeMarkerTemplateUtils.processTemplateIntoString(template, mailTemplateModel); } catch (Exception e) { e.printStackTrace(); log.error("模板静态化异常", e); return null; } }PS: .ftl模板文件放在 templates 目录下总结：freemarker无法使用类加载器获取jar包中的resourse目录下的templates文件解决办法：注入FreeMarkerConfigurer配置类，因为freemarker模板的默认目录就在resourse下的templates目录下，使用freeMarkerConfigurer.getConfiguration().getTemplate("mailTemplate.ftl")可直接获取到对应的模板文件

记一次解决RestTemplate无法解析api返回的xml数据问题
type: item
date: 2019-12-06T21:52:00+08:00
description: 最近看上了个本命域名，因为过期即将被注册局删除，于是就自己写了个api接口用来监控这个域名，等可以注册了就直接发邮件通知我 其中用到了阿里云域名的一个api，准备直接使用RestTemplate去请求这个接口，并将结果自动封装为Map集合方便进行进一步处理。等到框架搭好，代码写完，以为万事大吉的时候，程序却给我来了个惊喜： org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [text/plain;charset=GBK]很明显，上面提示接口返回的是text/plain文本类型数据，RestTemplate解析数据的时候没有找到合适的解析器，就抛了异常。WTF？ 我看阿里云的接口明明返回的是xml的数据啊......仔细再定睛一看，类型还真的是文本类型...我嘞个去，还有这种操作？ 这可怎么办？当时我没想太多，就直接将返回值保存为String类型，然后使用jackson直接解析成对象了，这样也能进行数据处理了;不过问题虽然暂时解决了，可终究不是好办法，RestTemplate本身就内置了jackson解析器，所以说肯定能直接让RestTemplate解析这个数据的;本着极客精神，今天有空就看了下源码：根据程序提示的异常信息：org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [text/plain;charset=GBK] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:123) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1001) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:984)先进入HttpMessageConverterExtractor.java中，于是看到如下逻辑HttpMessageConverter<?> messageConverter = (HttpMessageConverter)var4.next(); if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter)messageConverter; if (genericMessageConverter.canRead(this.responseType, (Class)null, contentType)) { if (this.logger.isDebugEnabled()) { ResolvableType resolvableType = ResolvableType.forType(this.responseType); this.logger.debug("Reading to [" + resolvableType + "]"); } return genericMessageConverter.read(this.responseType, (Class)null, responseWrapper); } } if (this.responseClass != null && messageConverter.canRead(this.responseClass, contentType)) { if (this.logger.isDebugEnabled()) { String cl…

Git分布式版本控制工具
type: item
date: 2019-10-27T20:30:04+08:00
description: 1 Git概述1.1 Git历史Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上（1991－2002年间）。 到 2002 年，整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。到了 2005 年，开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束，他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区（特别是 Linux 的缔造者 Linus Torvalds）基于使用 BitKeeper 时的经验教训，开发出自己的版本系统。 他们对新的系统制订了若干目标：速度简单的设计对非线性开发模式的强力支持（允许成千上万个并行开发的分支）完全分布式有能力高效管理类似 Linux 内核一样的超大规模项目（速度和数据量）1.2 Git与SVN对比SVN是集中式版本控制系统，版本库是集中放在中央服务器的，而开发人员工作的时候，用的都是自己的电脑，所以首先要从中央服务器下载最新的版本，然后开发，开发完后，需要把自己开发的代码提交到中央服务器。集中式版本控制工具缺点：服务器单点故障容错性差Git是分布式版本控制系统（Distributed Version Control System，简称 DVCS） ，分为两种类型的仓库：本地仓库和远程仓库本地仓库：是在开发人员自己电脑上的Git仓库 远程仓库：是在远程服务器上的Git仓库Clone：克隆，就是将远程仓库复制到本地Push：推送，就是将本地仓库代码上传到远程仓库Pull：拉取，就是将远程仓库代码下载到本地仓库1.3 Git工作流程工作流程如下：1．从远程仓库中克隆代码到本地仓库2．从本地仓库中checkout代码然后进行代码修改3．在提交前先将代码提交到暂存区4．提交到本地仓库。本地仓库中保存修改的各个历史版本5．修改完成后，需要和团队成员共享代码时，将代码push到远程仓库1.4 Git下载与安装下载地址： https://git-scm.com/download安装过程详见：https://ffis.me/experience/1441.html2 Git代码托管服务2.1 常用的Git代码托管服务前面已经知道了Git中存在两种类型的仓库，即本地仓库和远程仓库。那么我们如何搭建Git远程仓库呢？我们可以借助互联网上提供的一些代码托管服务来实现，其中比较常用的有GitHub、码云、GitLab等。Github（ 地址：https://github.com/ ）是一个面向开源及私有软件项目的托管平台，因为只支持Git 作为唯一的版本库格式进行托管，故名Github码云（地址： https://gitee.com/ ）是国内的一个代码托管平台，由于服务器在国内，所以相比于GitHub，码云速度会更快GitLab （地址： https://about.gitlab.com/ ）是一个用于仓库管理系统的开源项目，使用Git作为代码管理工具，并在此基础上搭建起来的web服务2.2 在码云注册账号要想使用码云的相关服务，需要注册账号（地址： https://gitee.com/signup ）2.3 登录码云并创建Git远程仓库注册完成后就可以使用刚刚注册的邮箱进行登录（地址： https://gitee.com/login ）登录成功后就可以创建Git远程仓库创建完成后可以查看仓库信息每个Git远程仓库都会对应一个网络地址，可以点击克隆/下载按钮弹出窗口并点击复制按钮获得这个网络地址当前创建的Git远程仓库对应的地址为：https://gitee.com/noisky/myRepo.git2.4 邀请其他用户成为仓库成员前面已经在码云上创建了自己的远程仓库，目前仓库成员只有自己一个人（身份为管理员）。在企业实际开发中，一个项目往往是由多个人共同开发完成的，为了使多个参与者都有权限操作远程仓库，就需要邀请其他项目参与者成为当前仓库的成员。3 Git常用命令3.1 环境配置当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的，因为每次Git提交都会使用该用户信息设置用户信息 git config --global user.name “noisky”git config --global user.email “xxx@ffis.me”查看配置信息git config --listgit config user.name通过上面的命令设置的信息会保存在~/.gitconfig（用户目录下）文件中3.2 获取Git仓库要使用Git对我们的代码进行版本控制，首先需…

mybatis03 - 多表查询、简单的SSM整合
type: item
date: 2019-10-21T20:50:24+08:00
description: 1 Mybatis多表查询1.1 一对一查询1.1.1 一对一查询的模型用户表和订单表的关系为，一个用户有多个订单，一个订单只从属于一个用户一对一查询的需求：查询一个订单，与此同时查询出该订单所属的用户1.1.2一对一查询的语句对应的sql语句：SELECT *,o.id oid FROM orders o, USER u WHERE o.uid = u.id;查询的结果如下：1.1.3 创建Order和User实体public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; //此处省略getter和setter } public class User { private int id; private String username; private String password; private Date birthday; //此处省略getter和setter }1.1.4 创建OrderMapper接口public interface OrderMapper { List<Order> findAll(); }1.1.5 配置OrderMapper.xml<mapper namespace="me.ffis.mapper.OrderMapper"> <resultMap id="orderMap" type="me.ffis.domain.Order"> <result column="uid" property="user.id"></result> <result column="username" property="user.username"></result> <result column="password" property="user.password"></result> <result column="birthday" property="user.birthday"></result> </resultMap> SELECT *,o.id oid FROM orders o, USER u WHERE o.uid = u.id </mapper>其中还可以配置如下：<resultMap id="orderMap" type="me.ffis.domain.Order"> <result property="id" column="id"></result> <result property="ordertime" column="ordertime"></result> <result property="total" column="total"></result> <association property="user" javaType="me.ffis.domain.User"> <result column="uid" property="id"></result> <result column="username" property="username"></result> <result column="password" property="password"></result> <result column="birthday" property="birthday"></result> </association> </resultMap>1.1.6 测试结果OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List<Order> all = mapper.findAll(); for(Order order : all){ System.out.println(order); }1.2 一对多查询1.2.1 一对多查询的模型用户表和订单表的关系为，一个用户有多个订单，一个订单只从属于一个用户一对多查询的需求：查询一个用户，与此同时查询出该用户具有的订单1.2.2 一对多查询的语句对应的sql语句：SELECT *,o.id oid FROM USER u, orders o WHERE u.id = o.uid;查询的结果如下：1.2.3 修改User实体public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; //此处省略getter和set…

mybatis02 - 核心配置文件深入
type: item
date: 2019-10-20T21:03:00+08:00
description: 1 Mybatis的Dao层实现1.1 传统开发方式1.1.1 编写UserDao接口public interface UserDao { List<User> findAll() throws IOException; }1.1.2 编写UserDaoImpl实现public class UserDaoImpl implements UserDao { public List<User> findAll() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> userList = sqlSession.selectList("userMapper.findAll"); sqlSession.close(); return userList; } }1.1.3 测试传统方式@Test public void testTraditionDao() throws IOException { UserDao userDao = new UserDaoImpl(); List<User> all = userDao.findAll(); System.out.println(all); }1.2 代理开发方式1.2.1 代理开发方式介绍采用 Mybatis 的代理开发方式实现 DAO 层的开发，这种方式是我们后面进入企业的主流。Mapper 接口开发方法只需要程序员编写Mapper 接口（相当于Dao 接口），由Mybatis 框架根据接口定义创建接口的动态代理对象，代理对象的方法体同上边Dao接口实现类方法。Mapper 接口开发需要遵循以下规范：1) Mapper.xml文件中的namespace与mapper接口的全限定名相同2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同1.2.2 编写UserMapper接口1.2.3 测试代理方式@Test public void testProxyDao() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获得MyBatis框架生成的UserMapper接口的实现类 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findById(1); System.out.println(user); sqlSession.close(); }1.3 知识小结MyBatis的Dao层实现的两种方式：手动对Dao进行实现：传统开发方式代理方式对Dao进行实现： **UserMapper userMapper = sqlSession.getMapper(UserMapper.class);** 2 MyBatis映射文件深入2.1 动态sql语句2.1.1 动态sql语句概述Mybatis 的映射文件中，前面我们的 SQL 都是比较简单的，有些时候业务逻辑复杂时，我们的 SQL是动态变化的，此时我们的 SQL 就不能满足要求了。参考的官方文档，描述如下：2.1.2 动态 SQL 之<if>我们根据实体类的不同取值，使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询，如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。 …