首页
关于
Search
1
BT宝塔面板免费使用专业版网站监控报表插件
276 阅读
2
Python批量校验两个文件夹里面的文件MD5
180 阅读
3
MySQL创建索引
110 阅读
4
欢迎使用 Typecho
92 阅读
5
更改宝塔nginx默认的日志格式
92 阅读
默认分类
Java
SpringBoot
MySQL
Linux
登录
/
注册
Search
标签搜索
MySQL
Linux
JAVA
Docker
JavaScript
JDK
Redis
CentOS
SQL
SpringBoot
HTTP
Python
CDN
IP
前端
Micky
累计撰写
57
篇文章
累计收到
1
条评论
今日撰写
0
篇文章
首页
栏目
默认分类
Java
SpringBoot
MySQL
Linux
页面
关于
用户登录
登录
注册
搜索到
8
篇与
Java
的结果
2023-04-03
怎么设计微服务接口才是好的
怎么设计微服务接口才是好的,最好的微服务是无感的,那就是你不知道你调用的是微服务接口还是本地的方法,你要朝着这个方向出发。所以本地方法怎么设计,那么微服务就是怎么设计,本地方法如果需要校验参数,那么你也要校验参数,本地方法可以不校验参数,交给上游调用者来保证参数的正确性也可以。本地方法报错了,那么会抛出异常,同理微服务也可以做到的可以拦截 feign 的异常接口做处理,这样服务调用者同样也可以进行异常捕获,非常的舒服。这样设计的好处,就是任何时候都可以把模块代码 都可以拆成微服务被调用。其实本地调用和远程调用就是调用方式不一样,一定要记住-仅仅是调用方式不一样,其他开发都一样。你一定要设计基本的框架,来做到无感。团队内部需要把微服务的接口作为好定义,暴露参数和异常的情况。如果你接入其他的部门的服务,那么就不要把他们当做微服务,而是外部接口作为调用,不能污染自己的服务模型。我以前设计的微服务架构,可以随意的合并或者拆分,也就是多个微服务仅仅通过 maven 的构建参数,就可以合并一起成为一个整体服务进行部署,也可以随时拆分成多个微服务进行部署,这样的好处就是,如果是个人开发,那么就可以做到前期单体部署,后期可以慢慢的拆开,微服务部署
2023年04月03日
6 阅读
0 评论
1 点赞
2022-10-11
Windows安装JDK
暂无简介
2022年10月11日
7 阅读
0 评论
0 点赞
2022-08-31
Java导出Word文档
Maven 加入如下坐标 <!--导出Docx--> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.0</version> </dependency> <!--导出Docx-->建议使用最新版 https://github.com/Sayi/poi-tl/** * word工具类 * * @author CoderKK */ public class WordUtil { /** * 根据模板填充内容生成word,并下载 * * @param templatePath word模板文件路径 * @param paramMap 替换的参数集合 */ public static void downloadWord(OutputStream out, InputStream templatePath, Map<String, Object> paramMap, Configure config) throws Exception { /* // 生成本地文件 Long time = System.currentTimeMillis(); // 生成的word格式 String formatSuffix = ".docx"; // 拼接后的文件名 String fileName = time + formatSuffix; //设置生成的文件存放路径,可以存放在你想要指定的路径里面 String rootPath = "D:/tmp/" + File.separator + "file/word/"; String filePath = rootPath + fileName; File newFile = new File(filePath); //判断目标文件所在目录是否存在 if (!newFile.getParentFile().exists()) { //如果目标文件所在的目录不存在,则创建父目录 newFile.getParentFile().mkdirs(); } */ // 读取模板templatePath并将paramMap的内容填充进模板,即编辑模板(compile)+渲染数据(render) XWPFTemplate template = XWPFTemplate.compile(templatePath, config).render( paramMap); // 生成本地文件 //out = new FileOutputStream(filePath);//输出路径(下载到指定路径) // 将填充之后的模板写入filePath //将template写到OutputStream中 template.write(out); out.flush(); out.close(); template.close(); } /** * 远程文件url 转为输入流 * @param urlOrPath 远程文件url * @return * @throws Exception */ public static InputStream byteByUrl(String urlOrPath) throws Exception { URL url = new URL(urlOrPath); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置超时间为3秒 conn.setConnectTimeout(3 * 1000); //防止屏蔽程序抓取而返回403错误 //conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); //得到输入流 return conn.getInputStream(); } }/** * WordController * @author CoderKK */ @RestController public class WordController { @GetMapping("/word-download") public void word(HttpServletResponse response) throws Exception { response.setContentType("application/octet-stream"); //设置文件名称 response.setHeader("Content-Disposition", "attachment;filename=" + System.currentTimeMillis() + ".docx"); //获取 resources 目录 本地&打Jar包都可用 路径前不需要加/ //InputStream inputStream = getClass().getClassLoader().getResourceAsStream("word/template.docx"); //路径前需要加/ 具体区别请看https://www.cnblogs.com/geek233/p/15817853.html InputStream inputStream = getClass().getResourceAsStream("/word/template.docx"); //获取远程文件 //InputStream inputStream = WordUtil.byteByUrl("https://blog.52ipc.top/word/template.docx"); // 创建用于插入数据的Map 实际开发要放在service层 Map<String, Object> paramMap = new HashMap<>(18); paramMap.put("title", "Hi, Word模板"); paramMap.put("uname", "小蜜蜂~"); List<Map<String,Object>> voList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Map<String,Object> vo = new HashMap<>(); vo.put("areaId" , String.valueOf(i)); vo.put("name" , "北京"+ i); voList.add(vo); } paramMap.put("area", voList); // 循环行表渲染策略 LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); // 插件绑定 Configure config = Configure.builder() //area 变量名 //policy 策略 .bind("area", policy) .build(); WordUtil.downloadWord(response.getOutputStream(), inputStream, paramMap, config); } }word模板文件路径如下 模板文件下载链接 https://blog.52ipc.top/word/template.docx模板内容如下图
2022年08月31日
79 阅读
0 评论
0 点赞
2022-08-31
MybatisPlus拦截器打印完整SQL、分页、自动填充处理
MybatisPlus拦截器打印完整SQLMybatisPlus 虽然也自带了一个打印 SQL 的配置,但是不方便查看,也没有时间统计mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl拦截器PrintSqlInterceptor/** * @author CoderKK * @date 2020-09-01 00:13 */ @Slf4j @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class PrintSqlInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = null; if (invocation.getArgs().length > 1) { parameter = invocation.getArgs()[1]; } String sqlId = mappedStatement.getId(); BoundSql boundSql = mappedStatement.getBoundSql(parameter); Configuration configuration = mappedStatement.getConfiguration(); long start = System.currentTimeMillis(); Object returnValue = invocation.proceed(); long time = System.currentTimeMillis() - start; showSql(configuration, boundSql, time, sqlId); return returnValue; } private static void showSql(Configuration configuration, BoundSql boundSql, long time, String sqlId) { Object parameterObject = boundSql.getParameterObject(); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); //替换空格、换行、tab缩进等 String sql = boundSql.getSql().replaceAll("[\\s]+", " "); if (parameterMappings.size() > 0 && parameterObject != null) { TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { sql = sql.replaceFirst("\\?", getParameterValue(parameterObject)); } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } } } } logs(time, sql, sqlId); } private static String getParameterValue(Object obj) { String value; if (obj instanceof String) { value = "'" + obj + "'"; } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(new Date()) + "'"; } else { if (obj != null) { value = obj.toString(); } else { value = ""; } } return value.replace("$", "\\$"); } private static void logs(long time, String sql, String sqlId) { StringBuilder sb = new StringBuilder() .append(" Time:").append(time) .append(" ms - ID:").append(sqlId) .append(StringPool.NEWLINE).append("Execute SQL:") .append(sql).append(StringPool.NEWLINE); log.info(sb.toString()); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties0) { } }MybatisPlus配置 MybatisPlusConfigpackage com.example.demojs.config; import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.example.demojs.common.PrintSqlInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Properties; @Configuration public class MybatisPlusConfig { /** * mybatis-plus分页插件 */ /* // 旧版 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false // paginationInterceptor.setOverflow(false); // 设置最大单页限制数量,默认 500 条,-1 不受限制 // paginationInterceptor.setLimit(500); // 开启 count 的 join 优化,只针对部分 left join paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } // 新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } // 如何区分新旧版呢?其实你把这两种代码都复制到你的项目里,哪个类存在就用哪个方法。 // 3.4.0版本对此部分有更新,如果是旧版本升级,会出现分页失效问题,同时idea会提示PaginationInterceptor过时,新版本改用了MybatisPlusInterceptor */ /** *自定义mybatis插件 注入方式一 */ /* @Bean public MybatisPlusInterceptor mybatisInterceptor() { MybatisPlusInterceptor mybatisInterceptor = new MybatisPlusInterceptor(); Properties properties = new Properties(); properties.setProperty("name", name); mybatisInterceptor.setProperties(properties); return mybatisInterceptor; } */ /** *自定义mybatis插件 注入方式二 */ @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { //插件拦截链采用了责任链模式,执行顺序和加入连接链的顺序有关 //MybatisInterceptor mybatisInterceptor = new MybatisInterceptor(); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); //设置参数,比如阈值等,可以在配置文件中配置 Properties properties = new Properties(); //properties.setProperty("name", name); mybatisPlusInterceptor.setProperties(properties); //自定义打印SQL PrintSqlInterceptor printSqlInterceptor = new PrintSqlInterceptor(); configuration.addInterceptor(printSqlInterceptor); }; } /** * mybatis-plus SQL执行效率插件【生产环境可以关闭】 3.3.2版本移除了该功能,3.0.3和3.0.3之前版本支持。 */ /* @Bean public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } */ }打印效果2022-08-29 16:42:03.782 INFO 17476 --- [nio-8080-exec-2] c.e.demojs.common.PrintSqlInterceptor : Time:397 ms - ID:com.example.demojs.dao.UserMapper.insert Execute SQL:INSERT INTO test_user ( id, user_id, phone, birthday, sex ) VALUES ( 1564171509361389569, 'abc', '13988776664', 2022-08-29T16:41:59.514, 1 )自动填充插件/** * @author CoderKK * @date 2020/9/6 15:21 * @desc 自动填充处理器类 */ @Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入时的填充策略 * * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } /** * 更新时的填充策略 * * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } } 实体类记得加注解 @TableField(value = "create_time", fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8") private LocalDateTime createTime; @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8") private LocalDateTime updateTime; 分页插件注入分页插件后,如何使用呢?mapper写法:// 这是mapper自己提供的方法,参数是wrapper 适用于单表查 <P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper); // 自定义sql,适用于多表联查 IPage<UserVO> queryUserList(Page<UserVO> page, @Param("dto") ConditionDTO conditionDTO);**
2022年08月31日
58 阅读
0 评论
0 点赞
2022-08-03
缓存和数据库的一致性保障
缓存和数据库的一致性保障最终方案:缓存三重删除+数据一致性校验+更新流程禁用缓存+强制读Redis主节点更新数据库后同步删除缓存监听数据库的binlog异步删除缓存:带重试,保障一定会最终删除成功缓存数据带过期时间,过期后自动删除,越近更新的数据过期时间越短主要用于进一步防止并发下的脏数据问题解决一些由于未知情况,导致需要更换缓存结构的问题监听数据库的binlog延迟N秒后进行数据一致性校验解决一些极端场景下的脏数据问题存在数据库更新的链路禁用对应缓存防止并发下短期内的脏数据影响到更新流程强制读Redis主节点查询异步数据一致性校验、灰度放量
2022年08月03日
5 阅读
0 评论
0 点赞
1
2