-
随便打开一个Microsoft Visual Studio,新建一个WinForms项目,从下面列表中随便选择一个NET框架。net35;net40;net45;net451;net452;net46;net461;net462;net47;net471;net472;net48;net481;netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0;创建完窗口后,切换到【程序包管理控制台】,输入下面命令并回车:Install-Package PaddleSegSharp -Version 1.0.0完成PaddleSegSharp的依赖包安装。在窗口界面拖放一个按钮,并双击,在按钮的点击事件中,输入以下代码:添加命名空间:using PaddleSegSharp;OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png"; if (ofd.ShowDialog() != DialogResult.OK) return; string modelPath = null; //参数 MattingParameter parameter = new MattingParameter(); //初始化引擎 PaddleSegMattingEngine engine = new PaddleSegMattingEngine(); engine.Init(modelPath, parameter); //设置背景颜色 engine.Setbackground(45, 145, 255); //分割后的文件 string outfile = "C:\\" + Guid.NewGuid().ToString() + ".bmp"; engine.Seg(ofd.FileName, outfile);然后按F5运行,点击按钮,选择一个本地图片,进行抠图。结果存在C盘下,打开即可看到效果图。 项目简介:本项目是一个基于百度飞桨PaddleSeg项目的人像分割模块而开发的.NET的工具类库。本项目中PaddleSeg.dll文件是基于开源项目最新发布版本PaddleSeg 2.7PaddleSeg的版本修改而成的C++动态库,基于opencv的x64编译而成的。PaddleSeg是基于飞桨PaddlePaddle的端到端图像分割套件,内置45+模型算法及140+预训练模型,支持配置化驱动和API调用开发方式,打通数据标注、模型开发、训练、压缩、部署的全流程,提供语义分割、交互式分割、Matting、全景分割四大分割能力,助力算法在医疗、工业、遥感、娱乐等场景落地应用。本项目只能在X64的CPU上编译和使用,只能在avx指令集上的CPU上使用。本项目支持飞桨人像抠图的所有模型。PP-MattingV2-512、PP-Matting-512、PP-Matting-1024、PP-HumanMatting、MODNet-MobileNetV2。 默认使用MODNet-MobileNetV2模型。项目目前支持以下.NET框架,可以说,覆盖范围很广。net35;net40;net45;net451;net452;net46;net461;net462;net47;net471;net472;net48;net481;netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0;项目开源地址:cid:link_1https://www.cnblogs.com/raoyutian/p/16983749.html 原文连接
-
package org.fh.util;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.util.Random;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * 说明:常用工具 * 作者:FH Admin * from:fhadmin.cn */public class Tools { /** * 随机生成六位数验证码 * @return */ public static int getRandomNum(){ Random r = new Random(); return r.nextInt(900000)+100000;//(Math.random()*(999999-100000)+100000) } /** * 随机生成四位数验证码 * @return */ public static int getRandomNum4(){ Random r = new Random(); return r.nextInt(9000)+1000; } /** * 随机生成两位数验证码 * @return */ public static int getRandomNum2(){ Random r = new Random(); return r.nextInt(90)+10; } /** * 检测字符串是否不为空(null,"","null") * @param s * @return 不为空则返回true,否则返回false */ public static boolean notEmpty(String s){ return s!=null && !"".equals(s) && !"null".equals(s); } /** * 检测字符串是否为空(null,"","null") * @param s * @return 为空则返回true,不否则返回false */ public static boolean isEmpty(String s){ return s==null || "".equals(s) || "null".equals(s); } /** * 字符串转换为字符串数组 * @param str 字符串 * @param splitRegex 分隔符 * @return */ public static String[] str2StrArray(String str,String splitRegex){ if(isEmpty(str)){ return null; } return str.split(splitRegex); } /** * 用默认的分隔符(,)将字符串转换为字符串数组 * @param str 字符串 * @return */ public static String[] str2StrArray(String str){ return str2StrArray(str,",\\s*"); } /** * 往文件里的内容 * @param filePath 文件路径 * @param content 写入的内容 */ public static void writeFile(String fileP,String content){ String filePath = String.valueOf(Thread.currentThread().getContextClassLoader().getResource(""))+"../../"; //项目路径 filePath = filePath.replaceAll("file:/", ""); filePath = filePath.replaceAll("%20", " "); filePath = filePath.trim() + fileP.trim(); if(filePath.indexOf(":") != 1){ filePath = File.separator + filePath; } try { OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(filePath),"utf-8"); BufferedWriter writer=new BufferedWriter(write); writer.write(content); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 往文件里的内容(Projectpath下) * @param filePath 文件路径 * @param content 写入的内容 */ public static void writeFileCR(String fileP,String content){ String filePath = PathUtil.getProjectpath() + fileP; try { OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(filePath),"utf-8"); BufferedWriter writer=new BufferedWriter(write); writer.write(content); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 验证邮箱 * @param email * @return */ public static boolean checkEmail(String email){ boolean flag = false; try{ String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; Pattern regex = Pattern.compile(check); Matcher matcher = regex.matcher(email); flag = matcher.matches(); }catch(Exception e){ flag = false; } return flag; } /** * 验证手机号码 * @param mobiles * @return */ public static boolean checkMobileNumber(String mobileNumber){ boolean flag = false; try{ Pattern regex = Pattern.compile("^(((13[0-9])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8})|(0\\d{2}-\\d{8})|(0\\d{3}-\\d{7})$"); Matcher matcher = regex.matcher(mobileNumber); flag = matcher.matches(); }catch(Exception e){ flag = false; } return flag; } /** * 检测KEY是否正确 * @param paraname 传入参数 * @param FKEY 接收的 KEY * @return 为空则返回true,不否则返回false */ public static boolean checkKey(String paraname, String FKEY){ paraname = (null == paraname)? "":paraname; return MD5.md5(paraname+DateUtil.getDays()+",fh,").equals(FKEY); } /**读取txt里的全部内容 * @param fileP 文件路径 * @param encoding 编码 * @return */ public static String readTxtFileAll(String fileP, String encoding) { StringBuffer fileContent = new StringBuffer(); try { String filePath = String.valueOf(Thread.currentThread().getContextClassLoader().getResource(""))+"../../"; //项目路径 filePath = filePath.replaceAll("file:/", ""); filePath = filePath.replaceAll("%20", " "); filePath = filePath.trim() + fileP.trim(); if(filePath.indexOf(":") != 1){ filePath = File.separator + filePath; } File file = new File(filePath); if (file.isFile() && file.exists()) { // 判断文件是否存在 InputStreamReader read = new InputStreamReader( new FileInputStream(file), encoding); // 考虑到编码格式 BufferedReader bufferedReader = new BufferedReader(read); String lineTxt = null; while ((lineTxt = bufferedReader.readLine()) != null) { fileContent.append(lineTxt); fileContent.append("\n"); } read.close(); }else{ System.out.println("找不到指定的文件,查看此路径是否正确:"+filePath); } } catch (Exception e) { System.out.println("读取文件内容出错"); } return fileContent.toString(); } /** * 读取Projectpath某文件里的全部内容 * @param filePath 文件路径 */ public static String readFileAllContent(String fileP) { StringBuffer fileContent = new StringBuffer(); try { String encoding = "utf-8"; File file = new File(PathUtil.getProjectpath() + fileP);//文件路径 if (file.isFile() && file.exists()) { // 判断文件是否存在 InputStreamReader read = new InputStreamReader( new FileInputStream(file), encoding); // 考虑到编码格式 BufferedReader bufferedReader = new BufferedReader(read); String lineTxt = null; while ((lineTxt = bufferedReader.readLine()) != null) { fileContent.append(lineTxt); fileContent.append("\n"); } read.close(); }else{ System.out.println("找不到指定的文件,查看此路径是否正确:"+fileP); } } catch (Exception e) { System.out.println("读取文件内容出错"); } return fileContent.toString(); } public static void main(String[] args) { System.out.println(getRandomNum()); }}
-
背景 最近好几个项目在运行过程中客户都提出文件上传大小的限制能否设置的大一些,用户经常需要上传好几个 G 的资料文件,如图纸,视频等,并且需要在上传大文件过程中进行优化实时展现进度条,进行技术评估后针对框架文件上传进行扩展升级,扩展接口支持大文件分片上传处理,减少服务器瞬时的内存压力,同一个文件上传失败后可以从成功上传分片位置进行断点续传,文件上传成功后再次上传无需等待达到秒传的效果,优化用户交互体验,具体的实现流程如下图所示 (java fhadmin.cn) 文件 MD5 计算 对于文件 md5 的计算我们使用 spark-md5 第三方库,大文件我们可以分片分别计算再合并节省时间,但是经测试 1G 文件计算 MD5 需要 20s 左右的时间,所以经过优化我们抽取文件部分特征信息(文件第一片 + 文件最后一片 + 文件修改时间),来保证文件的相对唯一性,只需要 2s 左右,大大提高前端计算效率,对于前端文件内容块的读取我们需要使用 html5 的 api 中 fileReader.readAsArrayBuffer 方法,因为是异步触发,封装的方法提供一个回调函数进行使用 //java fhadmin.cn createSimpleFileMD5(file, chunkSize, finishCaculate) { var fileReader = new FileReader(); var blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice; var chunks = Math.ceil(file.size / chunkSize); var currentChunk = 0; var spark = new SparkMD5.ArrayBuffer(); var startTime = new Date().getTime(); loadNext(); fileReader.onload = function() { spark.append(this.result); if (currentChunk == 0) { currentChunk = chunks - 1; loadNext(); } else { var fileMD5 = hpMD5(spark.end() + file.lastModifiedDate); finishCaculate(fileMD5) } }; function loadNext() { var start = currentChunk * chunkSize; var end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); } } 文件分片切割 我们通过定义好文件分片大小,使用 blob 对象支持的 file.slice 方法切割文件,分片上传请求需要同步按顺序请求,因为使用了同步请求,前端 ui 会阻塞无法点击,需要开启 worker 线程进行操作,完成后通过 postMessage 方法传递消息给主页面通知 ui 进度条的更新,需要注意的是,worker 线程方法不支持 window 对象,所以尽量不要使用第三方库,使用原生的 XMLHttpRequest 对象发起请求,需要的参数通过 onmessage 方法传递获取 页面 upload 请求方法如下 //java fhadmin.cn upload() { var file = document.getElementById("file").files[0]; if (!file) { alert("请选择需要上传的文件"); return; } if (file.size < pageData.chunkSize) { alert("选择的文件请大于" + pageData.chunkSize / 1024 / 1024 + "M") } var filesize = file.size; var filename = file.name; pageData.chunkCount = Math.ceil(filesize / pageData.chunkSize); this.createSimpleFileMD5(file, pageData.chunkSize, function(fileMD5) { console.log("计算文件MD:" + fileMD5); pageData.showProgress = true; var worker = new Worker('worker.js'); var param = { token: GetTokenID(), uploadUrl: uploadUrl, filename: filename, filesize: filesize, fileMD5: fileMD5, groupguid: pageData.groupguid1, grouptype: pageData.grouptype1, chunkCount: pageData.chunkCount, chunkSize: pageData.chunkSize, file: file } worker.onmessage = function(event) { var workresult = event.data; if (workresult.code == 0) { pageData.percent = workresult.percent; if (workresult.percent == 100) { pageData.showProgress = false; worker.terminate(); } } else { pageData.showProgress = false; worker.terminate(); } } worker.postMessage(param); }) } worker.js 执行方法如下 //java fhadmin.cn function FormAjax_Sync(token, data, url, success) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("post", url, false); xmlHttp.setRequestHeader("token", token); xmlHttp.onreadystatechange = function() { if (xmlHttp.status == 200) { var result = JSON.parse(this.responseText); var status = this.status success(result, status); } }; xmlHttp.send(data); } onmessage = function(evt) { var data = evt.data; console.log(data) //传递的参数 var token = data.token var uploadUrl = data.uploadUrl var filename = data.filename var fileMD5 = data.fileMD5 var groupguid = data.groupguid var grouptype = data.grouptype var chunkCount = data.chunkCount var chunkSize = data.chunkSize var filesize = data.filesize var filename = data.filename var file = data.file var start = 0; var end; var index = 0; var startTime = new Date().getTime(); while (start < filesize) { end = start + chunkSize; if (end > filesize) { end = filesize; } var chunk = file.slice(start, end); //切割文件 var formData = new FormData(); formData.append("file", chunk, filename); formData.append("fileMD5", fileMD5); formData.append("chunkCount", chunkCount) formData.append("chunkIndex", index); formData.append("chunkSize", end - start); formData.append("groupguid", groupguid); formData.append("grouptype", grouptype); //上传文件 FormAjax_Sync(token, formData, uploadUrl, function(result, status) { var code = 0; var percent = 0; if (result.code == 0) { console.log("分片共" + chunkCount + "个" + ",已成功上传第" + index + "个") percent = parseInt((parseInt(formData.get("chunkIndex")) + 1) * 100 / chunkCount); } else { filesize = -1; code = -1 console.log("分片第" + index + "个上传失败") } self.postMessage({ code: code, percent: percent }); }) start = end; index++; } console.log("上传分片总时间:" + (new Date().getTime() - startTime)); console.log("分片完成"); } 文件分片接收 前端文件分片处理完毕后,接下来我们详细介绍下后端文件接受处理的方案,分片处理需要支持用户随时中断上传与文件重复上传,我们新建表 f_attachchunk 来记录文件分片的详细信息,表结构设计如下 CREATE TABLE `f_attachchunk` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ChunkGuid` varchar(50) NOT NULL, `FileMD5` varchar(100) DEFAULT NULL, `FileName` varchar(200) DEFAULT NULL, `ChunkSize` int(11) DEFAULT NULL, `ChunkCount` int(11) DEFAULT NULL, `ChunkIndex` int(11) DEFAULT NULL, `ChunkFilePath` varchar(500) DEFAULT NULL, `UploadUserGuid` varchar(50) DEFAULT NULL, `UploadUserName` varchar(100) DEFAULT NULL, `UploadDate` datetime DEFAULT NULL, `UploadOSSID` varchar(200) DEFAULT NULL, `UploadOSSChunkInfo` varchar(1000) DEFAULT NULL, `ChunkType` varchar(50) DEFAULT NULL, `MergeStatus` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=237 DEFAULT CHARSET=utf8mb4; FileMD5:文件 MD5 唯一标识文件 FileName:文件名称 ChunkSize:分片大小 ChunkCount:分片总数量 ChunkIndex:分片对应序号 ChunkFilePath:分片存储路径(本地存储文件方案使用) UploadUserGuid:上传人主键 UploadUserName:上传人姓名 UploadDate:上传人日期 UploadOSSID:分片上传批次 ID(云存储方案使用) UploadOSSChunkInfo:分片上传单片信息(云存储方案使用) ChunkType:分片存储方式(本地存储,阿里云,华为云,Minio 标识) MergeStatus:分片合并状态(未合并,已合并) 文件分片存储后端一共分为三步,检查分片 =》保存分片 =》合并分片,我们这里先以本地文件存储为例讲解,云存储思路一致,后续会提供对应使用的 api 方法 检查分片 检查分片以数据库文件分片记录的 FIleMD5 与 ChunkIndex 组合来确定分片的唯一性,因为本地分片 temp 文件是作为临时文件存储,可能会出现手动清除施放磁盘空间的问题,所以数据库存在记录我们还需要对应的检查实际文件情况 boolean existChunk = false; AttachChunkDO dbChunk = attachChunkService.checkExistChunk(fileMD5, chunkIndex, "Local"); if (dbChunk != null) { File chunkFile = new File(dbChunk.getChunkFilePath()); if (chunkFile.exists()) { if (chunkFile.length() == chunkSize) { existChunk = true; } else { //删除数据库记录 attachChunkService.delete(dbChunk.getChunkGuid()); } } else { //删除数据库记录 attachChunkService.delete(dbChunk.getChunkGuid()); } } 保存分片 保存分片分为两块,文件存储到本地,成功后数据库插入对应分片信息 //获取配置中附件上传文件夹 String filePath = frameConfig.getAttachChunkPath() + "/" + fileMD5 + "/"; //根据附件guid创建文件夹 File targetFile = new File(filePath); if (!targetFile.exists()) { targetFile.mkdirs(); } if (!existChunk) { //保存文件到文件夹 String chunkFileName = fileMD5 + "-" + chunkIndex + ".temp"; FileUtil.uploadFile(FileUtil.convertStreamToByte(fileContent), filePath, chunkFileName); //插入chunk表 AttachChunkDO attachChunkDO = new AttachChunkDO(fileMD5, fileName, chunkSize, chunkCount, chunkIndex, filePath + chunkFileName, "Local"); attachChunkService.insert(attachChunkDO); } 合并分片 在上传分片方法中,如果当前分片是最后一片,上传完毕后进行文件合并工作,同时进行数据库合并状态的更新,下一次同一个文件上传时我们可以直接拷贝之前合并过的文件作为新附件,减少合并这一步骤的 I/O 操作,合并文件我们采用 BufferedOutputStream 与 BufferedInputStream 两个对象,固定缓冲区大小 if (chunkIndex == chunkCount - 1) { //合并文件 String merageFileFolder = frameConfig.getAttachPath() + groupType + "/" + attachGuid; File attachFolder = new File(merageFileFolder); if (!attachFolder.exists()) { attachFolder.mkdirs(); } String merageFilePath = merageFileFolder + "/" + fileName; merageFile(fileMD5, merageFilePath); attachChunkService.updateMergeStatusToFinish(fileMD5); //插入到附件库 //设置附件唯一guid attachGuid = CommonUtil.getNewGuid(); attachmentDO.setAttguid(attachGuid); attachmentService.insert(attachmentDO); } 云文件分片上传 云文件上传与本地文件上传的区别就是,分片文件直接上传到云端,再调用云存储 api 进行文件合并与文件拷贝,数据库相关记录与检查差异不大 阿里云 OSS 上传分片前需要生成该文件的分片上传组标识 uploadid 上传分片时需要指定 uploadid,同时我们要将返回的分片信息 PartETag 序列化保存数据库,用于后续的文件合并 //java fhadmin.cn 合并分片时通过传递保存分片的 PartETag 对象数组进行操作,为了附件独立唯一性我们不直接使用合并后的文件,通过 api 进行文件拷贝副本使用 public boolean merageFile(String uploadId, List<PartETag> chunkInfoList,String key,AttachmentDO attachmentDO,boolean checkMerge){ key = "chunk/" + key + "/" + key; boolean result = true; try{ TenantParams.attach appConfig = getAttach(); OSSClient ossClient = InitOSS(appConfig); String bucketName = appConfig.getBucketname_auth(); if(!checkMerge){ CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, key, uploadId, chunkInfoList); CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest); } String attachKey = getKey(attachmentDO); ossClient.copyObject(bucketName,key,bucketName,attachKey); ossClient.shutdown(); }catch (Exception e){ e.printStackTrace(); logger.error("OSS合并文件失败:" + e.getMessage()); result = false; } return result; } 华为云 OBS 华为云 api 与阿里云 api 大致相同,只有个别参数名称不同,直接上代码 //java fhadmin.cn Minio 文件存储 Minio 应用比较广泛,框架也同时支持了自己独立部署的 Minio 文件存储系统,Minio 没有对应的分片上传 api 支持,我们可以在上传完分片文件后,使用 composeObject 方法进行文件的合并 ——————————————— 原文作者:FH-Admin 转自链接:https://learnku.com/articles/64010 版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。
-
我们可以通过@TableId注解的 类型属性来设置主键id的增长策略,一共有几个多个主键策略,可根据情况自由配置。1,ASSIGN_ID(雪花算法) 如果不设置类型值,默认则使用IdType.ASSIGN_ID策略(自3.3.0起)。该策略会使用雪花算法自动生成主键ID,主键类型为长或字符串(分别对应的MySQL的表字段为BIGINT和VARCHAR)提示:该策略使用接口IdentifierGenerator的方法nextId(以实现类为DefaultIdentifierGenerator雪花算法),下面是雪花算法介绍:雪花算法(雪花)是微博开源的分布式ID生成算法其核心思想就是:使用一个64位的长型的数字作为全局唯一ID。在分布式系统中的应用十分广泛,且ID引入了时间戳,基本上保持自增的。1个2345678@Datapublic class UserInfo { //指定主键生成策略使用雪花算法(默认策略) @TableId(type = IdType.ASSIGN_ID) private Long id; private String userName; private String passWord;}2,ASSIGN_UUID(排除中划线的UUID) 如果使用IdType.ASSIGN_UUID策略,并重新自动生成排除中划线的UUID作为主键。主键类型为String,对应MySQL的表分段为VARCHAR(32)提示:该策略使用接口IdentifierGenerator的方法nextUUID1个2345678@Datapublic class UserInfo { //指定主键生成策略为不含中划线的UUID @TableId(type = IdType.ASSIGN_UUID) private String id; private String userName; private String passWord;}3,AUTO(数据库ID自增)对于像MySQL这样的支持主键自动递增的数据库,我们可以使用IdType.AUTO策略。1个2345678@Datapublic class UserInfo { //指定主键使用数据库ID自增策略 @TableId(type = IdType.AUTO) private Integer id; private String userName; private String passWord;}4,INPUT(插入前自行设置主键值)(1)针对有序列的数据库:Oracle,SQLServer等,当需要建立一个自增序列时,需要用到序列。提示:在Oracle 11g中,设置自增扩,需要先创建序列(SQUENCE)再创建一个触发器(TRIGGER)。在Oracle 12c中,只需要使用IDENTITY属性就可以了,和MySQL一样简单。(2)Mybatis -Plus已经定义好了常见的数据库主键序列,我们首先只需要在@Configuration类中定义好@Bean:Mybatis -Plus内置了如下数据库主键序列(如果内置支持不满足你的需求,可实现IKeyGenerator接口来进行扩展):DB2KeyGeneratorH2KeyGeneratorKingbaseKeyGeneratorOracle密钥生成器PostgreKeyGenerator1个234@Beanpublic OracleKeyGenerator oracleKeyGenerator(){ return new OracleKeyGenerator();}(3)然后实体类配置主键Sequence,指定主键策略为IdType.INPUT即可:提示:支持父类定义@KeySequence子类使用,这样就可以几个表共享一个Sequence1个234567@TableName("TEST_SEQUSER")@KeySequence("SEQ_TEST")//类注解public class TestSequser{ @TableId(value = "ID", type = IdType.INPUT) private Long id;}(4)如果主键是String类型的,也可以使用:如何使用序列作为主键,但是实体主键类型是字符串开头,表的主键是varchar2,但是需要从序列中取值实体定义@KeySequence注解clazz指定类型String.class实体定义主键的类型字符串注意:oracle的序列返回的是Long类型,如果主键类型是Integer,可能会引起ClassCastException1个234567@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)public class YourEntity{ @TableId(value = "ID_STR", type = IdType.INPUT) private String idStr; ...}5,无(无状态)如果使用IdType.NONE策略,表示未设置主键类型(注解里等于跟随上下,左右里约等于INPUT)附:变量策略配置 假设我们希望完全全部都使用AUTO策略(数据库ID自增),那么可以在 application.properties中添加如下配置进行修改:1个mybatis-plus.global-config.db-config.id-type=auto原文出自:www.hangge.com 转载请保留原文链接:cid:link_0
-
解决办法 报错原因:因为 @Mapper 是属于 mybatis,不需要在 spring 配置中设置扫描地址,通过 mapper.xml 里面的 namespace 属性对应相关的 mapper 类,spring 将动态的生成 Bean 后注入到 ServiceImpl 中。 看了很多同学们的在设置改掉 springbean 自动装配的办法,但是仍报错。可以选择在 mapper interface 上面,添加 @Repository 注解: 效果 @Mapper 与 @Repository 的相同点: 两个注解都可在 dao/mapper 层使用(用来标记持久层),生成相应代理对象 bean 注入到 spring 容器中。 @Mapper 与 @Repository 的不同点: @Mapper 是 mybatis 的注解,可以单独使用,也允许存在在多个 mapper 文件,它不需要在 Spring 中配置扫描地址,通过 xml 里面的 namespace 里面的接口地址,生成 Bean 后注入到 Service 层中。 @Repository 是 spring 提供的注解,能够将该类注册成 Bean。被依赖注入,但是使用该注解后,在启动类上要加 @MapperScan 来表名 Mapper 类的位置,否则扫描不到。但在 idea 中,使用 @Repository 可以消除在业务层中注入 mapper 对象时的错误。 ———————————————— 原文作者:lanruo127 转自链接:https://learnku.com/articles/71027 版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。
-
1. Nacos 升级到 2021.1 后,本地未启动 Nacos,则无法启动 SpringBoot。 背景 本来是用的 SpringBoot 2.1.0,和 Nacos 版本忘记了。 后来升级 SpringBoot 2.5.4,然后 Spring Cloud 那一些也需要升级,随后升级到了 Nacos 版本 2021.1。 升级完之后,将 bootstrap.yml 的 spring.cloud 那些注释掉,启动还是会报错,Nacos 连接失败。 未升级前只要将 spring.cloud nacos 那些注释掉,顶多启动的时候控制台报些错,但不会导致启动不了。 解决 在 bootstrap.yml 中,不需要将那些注释掉了,只需要加两个 enabled 的参数设置为 false 就可以了。 spring: application: name: keepAccount cloud: nacos: discovery: enabled: false server-addr: 127.0.0.1:8848 config: enabled: false server-addr: 127.0.0.1:8848 file-extension: yml 这样子,在启动就不会报错了。 2. 启动报错:When allowCredentials is true, allowedOrigins cannot contain the special value 之前的 CrossConfig 需要改一下,将 allowedOrigins("*") 改为 allowedOriginPatterns("*")。 @Configuration public class CrossConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // .allowedOrigins("*") .allowedOriginPatterns("*") .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600) .allowedHeaders("*"); } } 3. SpringCloud 版本升级后 bootstrap.yml 配置不生效 背景 和问题 1 有关联,因为 SpringBoot 升级,SpringCloud 也需要升级,当升级后重启 SpringBoot,发现 bootstrap.yml 中配置的应用端口号没生效,变成了默认的 8080. 问题原因 从 Spring Boot 2.4 版本开始,配置文件加载方式进行了重构。 另外也有配置的默认值变化,原来默认启用 true 现在变更为 false 如下: version:2.4 之前 package org.springframework.cloud.bootstrap; public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if ((Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) { } } } version:2.4.2 package org.springframework.cloud.util; public abstract class PropertyUtils { public static boolean bootstrapEnabled(Environment environment) { return (Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, false) || MARKER_CLASS_EXISTS; } } 解决办法 pom 文件中引入如下配置 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> 指定运行参数 spring.cloud.bootstrap.enabled 值为 true spring: cloud: bootstrap: enabled: true 原文作者:邢闯洋转自链接:https://learnku.com/articles/72031
-
打开 /etc/profile 文件 vim /etc/profile 在文件末尾添加以下内容 export JAVA_HOME=/usr/local/jdk/jdk8 export PATH=${JAVA_HOME}/bin:$PATH 执行下面这条命令source /etc/profile 命令行输入java,看到如下界面,则证明设置成功
-
java 的transient关键字的作用是需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。用法@transient 就是在给某个javabean上需要添加个属性,但是这个属性你又不希望给存到数据库中去,仅仅是做个临时变量,用一下。不修改已经存在数据库的数据的数据结构。那么这个注解就可以一用。只要在你准备添加的临时属性上添加这个注解,然后getter和setter自动完成一下,就可以啦。@Document(collection = "streams") public class Stream implements Serializable,Cloneable { private static final long serialVersionUID = 1L; @Transient private boolean alarmsettingsActive = false; xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx }12345678上面的是个人理解,下面呢就是详细说说。transient的作用 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。 总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。transient使用小结1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。作者:fireflies爱人如己原文:cid:link_2
-
1、配置pom文件 <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.9</version> </dependency> 2、配置模板 3、编码 4、效果 response.reset(); try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { ExcelWriter build = EasyExcel.write(out).withTemplate(classPathResource.getFile()).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); //配置使用上一个行的样式 WriteSheet writeSheet1 = EasyExcel.writerSheet().registerWriteHandler(new MyHandler()).build(); FillConfig fillConfig1 = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); //有集合需要写,则forceNewRow(true) FillConfig fillConfig2 = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); Map map = JSONObject.parseObject(JSONObject.toJSONString(byId), Map.class); build.fill(map, fillConfig1, writeSheet1); for (int i = 0; i < byId.getSugarCookingProcessEntities().size(); i++) { byId.getSugarCookingProcessEntities().get(i).setId(i + 1L); } List list = JSONObject.parseObject(JSONObject.toJSONString(byId.getSugarCookingProcessEntities()), List.class); build.fill(new FillWrapper("sugarCookingProcessEntities", list), fillConfig2, writeSheet1); build.fill(new FillWrapper("sugarCookingFeedingEntities", JSONObject.parseObject(JSONObject.toJSONString(byId.getSugarCookingFeedingEntities()), List.class)), fillConfig2, writeSheet1); IOUtils.write(out.toByteArray(), response.getOutputStream()); } catch (Exception e) { log.error("导出数据" + byId.getOrderId() + "失败", e); } ———————————————— 原文链接:https://blog.csdn.net/Z_linht/article/details/118698643
-
public class FileUtil { public static InputStream getResourcesFileInputStream(String fileName) { return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName); } public static String getPath() { return FileUtil.class.getResource("/").getPath(); } public static File createNewFile(String pathName) { File file = new File(getPath() + pathName); if (file.exists()) { file.delete(); } else { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } return file; } public static File readFile(String pathName) { return new File(getPath() + pathName); } } 我查询并需导出的数据,并且里面的字段和excel需要导出的字段对应 -------------------------------------------------华丽的分割线------------------------------------------------------------- 大神能看到这里,感激不经,废话不多,请过目 这里是报错行: ExcelWriter excelWriter = EasyExcel.write(bos).withTemplate(templateFileName).build(); 1 2 com.alibaba.excel.exception.ExcelGenerateException: Create workbook failure at com.alibaba.excel.context.WriteContextImpl.<init>(WriteContextImpl.java:87) at com.alibaba.excel.write.ExcelBuilderImpl.<init>(ExcelBuilderImpl.java:35) at com.alibaba.excel.ExcelWriter.<init>(ExcelWriter.java:47) at com.alibaba.excel.write.builder.ExcelWriterBuilder.build(ExcelWriterBuilder.java:130) at cn.gaialab.gaiamind.microservice.brp.service.ecxport.impl.PlanVisitDataServiceImpl.exportRoutePlanningStore(PlanVisitDataServiceImpl.java:121) at cn.gaialab.gaiamind.microservice.brp.web.ecxport.ExportCustomerInfoController.exportRoutePlanningStore1(ExportCustomerInfoController.java:41) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF) at org.apache.poi.openxml4j.opc.internal.ZipHelper.verifyZipHeader(ZipHelper.java:147) at org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(ZipHelper.java:175) at org.apache.poi.openxml4j.opc.ZipPackage.<init>(ZipPackage.java:104) at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:307) at org.apache.poi.ooxml.util.PackageHelper.open(PackageHelper.java:37) at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:303) at com.alibaba.excel.util.WorkBookUtil.createWorkBook(WorkBookUtil.java:32) at com.alibaba.excel.context.WriteContextImpl.<init>(WriteContextImpl.java:85) ... 55 common frames omitted 2020-03-24 15:00:49.261 INFO 11336 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SERVICE-BRP/${eureka.instance.hostname}:service-brp:7081 - Re-registering apps/SERVICE-BRP 2020-03-24 15:00:49.261 INFO 11336 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SERVICE-BRP/${eureka.instance.hostname}:service-brp:7081: registering service... 2020-03-24 15:00:49.266 INFO 11336 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SERVICE-BRP/${eureka.instance.hostname}:service-brp:7081 - registration status: 204 2020-03-24 15:02:23.933 INFO 11336 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration 感言 感谢大家的帮助,问题已经解决,并且重新写了一篇,希望能给看到这的朋友,送上一点绵薄之力。 ———————————————— 原文链接:https://blog.csdn.net/weixin_42153503/article/details/104994360
-
当我们在service方法里面进行方法的自调用时,会发生事务失效的情况,这个时候可以通过AopContext.currentProxy()来获取当前类的代理类。 AopContext.currentProxy()是通过ThreadLocal来实现的。 /** * Class containing static methods used to obtain information about the current AOP invocation. * * <p>The {@code currentProxy()} method is usable if the AOP framework is configured to * expose the current proxy (not the default). It returns the AOP proxy in use. Target objects * or advice can use this to make advised calls, in the same way as {@code getEJBObject()} * can be used in EJBs. They can also use it to find advice configuration. * * <p>Spring's AOP framework does not expose proxies by default, as there is a performance cost * in doing so. * * <p>The functionality in this class might be used by a target object that needed access * to resources on the invocation. However, this approach should not be used when there is * a reasonable alternative, as it makes application code dependent on usage under AOP and * the Spring AOP framework in particular. * * @author Rod Johnson * @author Juergen Hoeller * @since 13.03.2003 */ public abstract class AopContext { /** * ThreadLocal holder for AOP proxy associated with this thread. * Will contain {@code null} unless the "exposeProxy" property on * the controlling proxy configuration has been set to "true". * @see ProxyConfig#setExposeProxy */ private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy"); /** * Try to return the current AOP proxy. This method is usable only if the * calling method has been invoked via AOP, and the AOP framework has been set * to expose proxies. Otherwise, this method will throw an IllegalStateException. * @return Object the current AOP proxy (never returns {@code null}) * @throws IllegalStateException if the proxy cannot be found, because the * method was invoked outside an AOP invocation context, or because the * AOP framework has not been configured to expose the proxy */ public static Object currentProxy() throws IllegalStateException { Object proxy = currentProxy.get(); if (proxy == null) { throw new IllegalStateException( "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available."); } return proxy; } /** * Make the given proxy available via the {@code currentProxy()} method. * <p>Note that the caller should be careful to keep the old value as appropriate. * @param proxy the proxy to expose (or {@code null} to reset it) * @return the old proxy, which may be {@code null} if none was bound * @see #currentProxy() */ @Nullable static Object setCurrentProxy(@Nullable Object proxy) { Object old = currentProxy.get(); if (proxy != null) { currentProxy.set(proxy); } else { currentProxy.remove(); } return old; } } ———————————————— 原文链接:https://blog.csdn.net/weixin_41751625/article/details/117038828
-
常规使用SpringAOP功能,都是对一个Service中的B方法进行切入记录日志,这些时候AOP是能起作用的。但是假如B方法被Service中的A方法调用,在B方法上的切入便会失效,导致无法记录日志。 要弄清楚切入失败的原因,就要先了解切入的原理。为什么能够切入目标对象,原理就是创建了代理类,在代理类中调用目标方法的前后进行切入。比如说目标对象是service,代理对象是$proxy0,这时候切入对B方法的调用就是$proxy0.B(),执行流程就是先执行切面中的逻辑比如记录日志,然后再invoke调用service的B方法,所以可以切入;但是执行A方法$proxy0.A()只能对A方法起作用,A里面再使用this调用B的时候使用的是service的原目标对象s.(),而不是$proxy0.B(),所以对B的切入无效,因为压根就没有用代理对象去调用,自然不能使用代理对象提供的切面增强功能。 解决方案就是把service目标对象的A方法中对B方法的调用(this.B())改成使用代理对象对B的调用,那么怎么获取代理对象呢?AopContext.currentProxy()使用了保存了代理对象因此在A方法中使用【((Service) AopContext.currentProxy()).B()】就能解决切入失效的问题。 另外,如果使用【AopContext.currentProxy()】方法获取当前代理对象需要在类上添加【@EnableAspectJAutoProxy(exposeProxy=true)】注解 exposeProxy:在AopContext中暴露代理对象。指示代理应由 AOP 框架公开为ThreadLocal以通过AopContext类进行检索。 默认关闭,即不保证可访问的AopContext。 ———————————————— 原文链接:https://blog.csdn.net/qq_16159433/article/details/120952972
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签