代码类
前端
elementUI
前后端交互出现的问题
- 使用window.open的get方式传入参数到后端接口时出现AES解密报非16倍数的错误
前端:其中Encrypt是前端AES加密,方法可在utils见
let req = freeUser+","+freeUserPass+","+new Date().getTime()
window.open(baseUrl+'/ivasys/freeLogin?freeUsers='+Encrypt(req),'_blank')
后端:desEncryptTY是java后端解密方法见utils
try {
if(!StrUtils.isEmpty(freeUsers)){
freeUsers = AesEncryptUtil.desEncryptTY(freeUsers).trim();
Users = freeUsers.split(",");
}else{
logger.info("传入参数为空");
return new RedirectView(uri, true, false);
}
} catch (Exception e) {
e.printStackTrace();
}
解决方案:
前端url的base64加密(aes以16字节为组,用base64加密)中出现“+”等字符在地址中可能会被解析为空格
用urlencode加密,urlencode会将特殊符号加入
后端解密即可
⚠️:urlencode
该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ’ ( ) 。
该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 函数是不会进行转义的:;/?😡&=+$,#
⚠️:上面的都解决不了都话还有一种方法:
在try里面加个替换空格为+ 的操作
该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 函数是不会进行转义的:;/?😡&=+$,#
代码:
前端
let req = freeUser+","+freeUserPass+","+new Date().getTime()
window.open(baseUrl+'/ivasys/freeLogin?freeUsers='+encodeURI(Encrypt(req)),'_blank')
后端:
try {
freeUsers = URLDecoder.decode(freeUsers,"UTF-8");
} catch (UnsupportedEncodingException e) {
logger.info("URL解密失败");
e.printStackTrace();
}
vue双向绑定失效
出现原因1: 表单绑定失败
解决方法如下:
this.$set(this.form,"phone",this.$route.query.mobile)
或者:
将this用vm替代
vm.$forceUpdate();
出现原因2: 页面返回参数
params.callback();
删除该行
table组件封装
el-tab
实现功能:el-tab同一个页面标签页的切换
如:a和b页面,从b页面的某个dialog的按钮跳转到a子页面
b页面按钮方法:
handleConfig:function (data){
let vm = this
vm.containerVisible = false;
vm.$router.push({path:'a页面路径?activeName=first&containerName='+data.row.onlyName});
this.reload() //通过reload刷新返回到当前页面
},
reload刷新页面不会将地址的参数等清除
此时a页面的搜索栏会自动带上变量值进行搜索
a页面方法:
mounted() {
this.showface();
},
showface(){
if(this.$route.query.activeName!=null){
this.activeName = this.$route.query.activeName;
this.containerName = this.$route.query.containerName;
this.loading = true;
this.$refs.Container.refresh(this.containerName); //这里写一些跳转后必须要执行的函数
}
},
localStorage
Object.assign()与JSON.parse区别
this.$set(this.dataVirtual, ‘yunIp’, resData.res.data[0].ip)
set代码: //这里注意每个参数格式都要转换后才能被存储
localStorage.setItem('areasTree',JSON.stringify(res.res))
get代码:
this.tableTreeDdata = []
let treeData = JSON.parse(localStorage.getItem('areasTree'))
if(treeData!=null && treeData!=undefined){
this.tableTreeDdata.push(treeData)
}
el-collapse折叠
template:
<el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item title="算法挂载目录" name="5">
<div v-for="(item,index) in dataForm.algs" >
<el-row>
<el-col :span="11"><el-form-item
label-width="10px"
label=" "
:key="'domainensalg'+ index"
:prop="'algs.'+ index+'.volume'"
:rules="{required: true, message: '宿主机目录不能为空', trigger: 'blur'}">
<el-input v-model="item.volume" placeholder="请输入宿主机目录" style="width: 280px;float: left"></el-input>
</el-form-item></el-col>
<el-col :span="2">-</el-col>
<el-col :span="11"><el-form-item
label-width="10px"
label=" "
:key="'domainensalg'+ index"
:prop="'algs.'+ index+'.volumeMount'"
:rules="{required: true, message: '镜像目录不能为空', trigger: 'blur'}">
<el-input v-model="item.volumeMount" placeholder="请输入镜像目录" style="width: 280px;float: left"></el-input>
</el-form-item></el-col>
</el-row>
</div>
<el-button class="rightButton" icon="fa fa-plus" type="primary" size="small" @click="addCmd(5)" />
<el-button class="leftButton" icon="fa fa-minus" type="primary" size="small" @click="delCmd('algs')" />
</el-collapse-item>
</el-collapse>
data:
activeNames: ['1','3'],
methods:
addCmd(index) {
if(index==1){
vm.dataForm.cmds.push({cmd: ''})
}else if(index==2){
vm.dataForm.envs.push({name:'',value:''})
}else if(index==3){
vm.dataForm.ips.push({ip:'',host:''})
}else if(index==4){
vm.dataForm.basics.push({name:'',volume:'',volumeMount:'',readOnly:"true"})
}else if(index==5){
vm.dataForm.algs.push({volume:'',volumeMount:''})
}else if(index==6){
vm.dataForm.algServices.push({volume:'',volumeMount:''})
}
},
delCmd(type) {
if (vm.dataForm[type].length> 0) {
vm.dataForm[type].splice(0, 1)
}
},
refs表单
重置表单validate
this.$refs['dataForm'].resetFields()
指定清除某表单字段验证
this.$refs["字段"].clearValidate()
el-tree
实现效果如下:
template:
<el-input placeholder="输入关键字进行过滤" v-model="filterText">
</el-input>
<el-tree class="filter-tree" :data="tableTreeDdata" :props="defaultProps" node-key="code"
default-expand-all :filter-node-method="filterNode" @node-click="handleNodeClick" ref="tree">
</el-tree>
js:
import PopupTreeInput from "@/components/PopupTreeInput"//见下面的组件封装代码
data:
filterText: '',
tableTreeDdata: [],
defaultProps: {
children: 'children',
label: 'name'
},
methods:
filterText(val) {
this.$refs.tree.filter(val);
}
filterNode(value, data) {
if(!value) return true;
return data.name.indexOf(value) !== -1;
},
handleNodeClick: function(data, node, obj) {
if(data.code == 0 || data.code == "0")
data.code = null
this.filters.areaCode = data.code;
this.findPage(null)
},
popTree代码封装:
<template>
<div>
<el-popover ref="popover" :placement="placement" trigger="click">
<el-tree
class="treeinput"
:data="data"
:props="props"
node-key="nodeKey"
ref="popupTree"
@current-change="currentChangeHandle"
:default-expand-all="defaultExpandAll"
:highlight-current="true">
</el-tree>
</el-popover>
<el-input v-model="prop" v-popover:popover :readonly="true" :placeholder="placeholder" style="cursor:pointer;"></el-input>
</div>
</template>
<script>
export default {
name: 'PopupTreeInput',
props: {
data: {
type: Array,
default: []
},
props: {
type: Object,
default: {}
},
prop: {
type: String,
default: ''
},
nodeKey: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '点击选择内容'
},
placement: {
type: String,
default: 'bottom-start'//top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end
},
defaultExpandAll: {
type: Boolean,
default: false
},
currentChangeHandle: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
.treeinput{
width:200px;
height:200px;
overflow:scroll;
}
</style>
el-tree的另一种封装-实现效果如下:
表单:
<el-form-item label="所属地区" prop="areaCode">
<SelectTree
:props="props"
:options="optionData"
:value="''+dataForm.areaCode"
:clearable="isClearable"
:isRoot="isRoot"
:accordion="isAccordion"
@getValue="getValue($event)"
/>
</el-form-item>
data:
isRoot: true,
isClearable: true, // 可清空(可选)
isAccordion: true, // 可收起(可选)
props: {
// 配置项(必选)
value: "id",
label: "name",
children: "children"
// disabled:true
},
computed:
computed: {
/* 转树形数据 */
optionData() {
let cloneData = JSON.parse(JSON.stringify(this.treeList)); // 对源数据深度克隆
return cloneData.filter(father => {
// 循环所有项,并添加children属性
let branchArr = cloneData.filter(child => father.id == child.parentId); // 返回每一项的子级数组
branchArr.length > 0 ? (father.children = branchArr) : ""; //给父级添加一个children属性,并赋值
return father.parentId == 0; //返回第一层
});
}
},
methods:
getValue(value) {
this.dataForm.areaCode = value;
},
//data赋值
// 获取数据
findTreeData: function() {
this.tableTreeDdata = []
let treeData = JSON.parse(localStorage.getItem('areasTree'))
if(treeData!=null && treeData!=undefined) {
this.tableTreeDdata.push(treeData)
}else{
var req = {level:2}
this.$api.area.findTree(req).then((res) => {
if(res.res!=null){
localStorage.setItem('areasTree',JSON.stringify(res.res))
this.tableTreeDdata.push(res.res)
}
this.editLoading = false
})
}
},
selectTree组件封装:
<template>
<el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" ref="select">
<el-input
class="selectInput"
:placeholder="placeholder"
v-model="filterText">
</el-input>
<el-option :value="valueTitle" :label="valueTitle" class="options">
<el-tree id="tree-option"
ref="selectTree"
:accordion="accordion"
:data="options"
:props="props"
:node-key="props.value"
:default-expanded-keys="defaultExpandedKey"
:filter-node-method="filterNode"
@node-click="handleNodeClick">
</el-tree>
</el-option>
</el-select>
</template>
<script>
export default {
name: "el-tree-select",
props:{
/* 配置项 */
props:{
type: Object,
default:()=>{
return {
value:'id', // ID字段名
label: 'title', // 显示名称
children: 'children' // 子级字段名
}
}
},
/* 选项列表数据(树形结构的对象数组) */
options:{
type: Array,
default: ()=>{ return [] }
},
/* 初始值 */
value:{
type: String,
default: ()=>{ return null }
},
/* 可清空选项 */
clearable:{
type:Boolean,
default:()=>{ return true }
},
/* 自动收起 */
accordion:{
type:Boolean,
default:()=>{ return false }
},
/* 选择根节点 */
isRoot:{
type:Boolean,
default:()=>{ return false }
},
placeholder:{
type:String,
default:()=>{return "检索关键字"}
}
},
data() {
return {
filterText: '',
valueId:this.value, // 初始值
valueTitle:'',
defaultExpandedKey:[]
}
},
mounted(){
this.initHandle()
},
methods: {
// 初始化值
initHandle(){
if(this.valueId){
var node = this.$refs.selectTree.getNode(this.valueId)
if(node){
this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label] // 初始化显示
this.$refs.selectTree.setCurrentKey(this.valueId) // 设置默认选中
this.defaultExpandedKey = [this.valueId] // 设置默认展开
}else{
this.clearHandle() // 如果valueId不存在,清除选中
}
} else {
this.clearHandle()
}
this.initScroll()
},
// 初始化滚动条
initScroll(){
this.$nextTick(()=>{
let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]
let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')
scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'
scrollBar.forEach(ele => ele.style.width = 0)
})
},
// 切换选项
handleNodeClick(node){
if (this.isRoot) {
this.valueTitle = node[this.props.label]
this.valueId = node[this.props.value]
this.$emit('getValue',this.valueId)
this.defaultExpandedKey = []
} else {
this.valueTitle = node[this.props.label]
this.valueId = node[this.props.value]
this.$emit('getValue',this.valueId)
this.defaultExpandedKey = []
}
},
// 清除选中
clearHandle(){
this.valueTitle = ''
this.valueId = null
this.defaultExpandedKey = []
for (let i = 0; i < this.options.length; i++) {
let nodes = this.$refs.selectTree.store.nodesMap[this.options[i][this.props.value]]
if(nodes){
this.$refs.selectTree.store.nodesMap[this.options[i][this.props.value]].expanded = false
}
// if (typeof(this.$refs.selectTree.store.nodesMap[this.options[i][this.props.value]].expanded) != undefined) {
// this.$refs.selectTree.store.nodesMap[this.options[i][this.props.value]].expanded = false
// }
}
this.clearSelected()
this.$emit('getValue',null)
},
/* 清空选中样式 */
clearSelected(){
let allNode = document.querySelectorAll('#tree-option .el-tree-node')
allNode.forEach((element)=>element.classList.remove('is-current'))
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
}
},
watch: {
value(){
this.valueId = this.value
this.$refs.select.handleClose()
this.initHandle()
},
filterText(val) {
this.$refs.selectTree.filter(val);
}
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item{
height: auto;
max-height: 274px;
padding: 0;
overflow: hidden;
overflow-y: auto;
}
.el-select-dropdown__item.selected{
font-weight: normal;
}
ul li >>>.el-tree .el-tree-node__content{
height:auto;
padding: 0 20px;
}
.el-tree-node__label{
font-weight: normal;
}
.el-tree >>>.is-current .el-tree-node__label{
color: #409EFF;
font-weight: 700;
}
.el-tree >>>.is-current .el-tree-node__children .el-tree-node__label{
color:#606266;
font-weight: normal;
}
.selectInput{
padding: 0 5px;
box-sizing: border-box;
}
</style>
父子调用,相互传值
父页面:
template:
<ConConfig v-if="configCreate" ref="ConConfig" @listen-add-config="listenAddConfig" @closeListDialog="closeListDialog"> </ConConfig>
js:
import ConConfig from "@/views/ConConfig"
export default {
components: {
ConConfig,
},
data...
}
data:
configCreate:false,
父页面收到子页面的回调方法:
listenAddConfig:function(){
if(this.$refs.ConConfig.newData)
this.initConfig(this.yunId,this.imgName,this.versions)
},
父页面调用子页面方法:
//新增对话框
handleAddConfig: function() {
this.configCreate = true
//未避免执行顺序乱序
var st=setTimeout(() => {
this.$refs.ConConfig.open(this.yunId,this.imgName,this.versions);
},0)
},
子页面open方法:
open(yunId,imgName,versions) {
this.imgName= imgName
this.versions=versions
this.yunId = yunId
this.operation = true
this.tableVisible = false
this.dialogVisible = true
},
子页面回传值给父页面:
this.newData = true
this.$emit('listen-add-config', this.newData);
js LocalStorage
js Array filter()
返回数组 ages 中所有元素都大于 18 的元素:
var ages = [32, 33, 16, 40];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.filter(checkAdult);
}
vue实例:
let filterType = this.abilitys.filter((x) => {
return x.value == row.ability;
})
js search 方法:
var str="Visit Runoob!";
var n=str.search("Runoob");
vue实例:
this.filerAbilitys = this.abilitys.filter((x) => {
return x.type.search(obj) != -1;
})
页面刷新
export default {
inject: ['reload'],
}
方法调用:
this.reload()
进度条el-progress
template-el-table:
<el-table-column prop="startTime" label="任务进度" minWidth="120" header-align="center" align="center">
<template slot-scope="scope">
<el-progress :text-inside="true" :stroke-width="18" :percentage="progressFormat(scope.$index,scope.row)" :color="scope.row.color">
</el-progress>
</template>
</el-table-column>
methods:
progressFormat: function(indexTest, row) {
var systime = new Date();
row.color = 'green';
var percent = 100;
var jiange = 0;
if(row.status == 0) {
row.color = 'blue';
if(row.type == 4 || row.type == 1 || row.type == 8) {
jiange = 10 * 1000;
var start = new Date(row.startTime)
var index = Math.ceil((systime.getTime() - start.getTime()) / jiange);
if(index < 0)
index = 1;
if(index > 99)
index = 99;
percent = index;
} else {
jiange = 2 * 1000;
percent = 99;
}
//lun = window.setInterval(lunxun, jiange);
} else if(row.status == 2 || row.status == '2') {
row.color = 'red';
} else
row.color = 'green';
return percent;
},
页面跳转
一:js的跳转
1.直接跳转:window.location.href
<script language="javascript" type="text/javascript">
window.location.href="login.jsp?backurl="+window.location.href;
//或者
window.location.href='http://www.baidu.com';
</script>
2.回到上一层页面 window.history.back(-1)
<script language="javascript">
//标签嵌套:
<a href="javascript:history.go(-1)">返回上一步</a>
</script>
二:vue跳转
1.在template中的常见写法:
<router-link to="/miniCard/statement/horizon">
<button class="btn btn-default">点击跳转</button>
</router-link>
2.this.$router.go()
this.$router.go(1) //在浏览器记录中前进一步
this.$router.go(-1) //后退一步记录
this.$router.go(3) //前进三步记录
3.this.$router.push()
A:this.$router.push({ path: '/home', query: { site: '1111'} })
query传参,用path属性对应跳转路径,类似于get提交,参数是在路径里显示的。
子页面接收时 var cityId = this.$route.query.cityId
B:this.$router.push({ name: 'Home', params: { site: '2222'} })
params传参,用name属性对应跳转路径,类似于post提交,参数不会出现在跳转路径里。
子页面接收时 var cityId = this.$route.params.cityId
两个同级页面,用query传参。A通过路由带参跳转到B页面,然后通过参数过滤掉B页面的一些数据。
之后刷新B页面,由于参数是在路径里的,还是过滤掉的数据,这个时候要么在B页面入口进 入B页面,要么就得在页面再做处理才能符合需求,改用params之后就没这个问题了。
- this.$router.replace() 用法同上
打开新的页面,不会像history添加新纪录,而是直接替换掉当前记录。点击返回,会跳转到上上一个页面。上一个记录是不存在的。
批量导入导出
template:
<el-form-item>
<kt-button
icon="fa fa-arrow-circle-right"
:label="$t('action.export')"
perms="openapi:excel:pmexport"
type="primary"
:loading="exportLoading"
@click="exportExcel"
/>
</el-form-item>
<el-form-item>
<el-upload
style="display: none"
action
ref="upload"
:http-request="importExcel"
:multiple="false"
:auto-upload="true"
:show-file-list="false"
:limit="1"
accept=".xls, .xlsx"
:with-credentials="true"
>
<el-button ref="uploadBtn" />
</el-upload>
<kt-button
icon="fa fa-arrow-circle-left"
:label="$t('action.import')"
perms="openapi:excel:pmimport"
type="primary"
:loading="importLoading"
@click="triggerUpload"
/>
</el-form-item>
js:
//导出Excel文件
exportExcel() {
this.exportLoading = true;
//1、获取总查询数据
let httpCode = this.filters.httpCode == "" ? null : this.filters.httpCode;
let httpName = this.filters.httpName == "" ? null : this.filters.httpName;
if ('_' == httpCode) {
httpCode = '/_'
}
if ('_' == httpName) {
httpName = '/_'
}
//筛选条件
const para = {
version: this.filters.version == "" ? null : this.filters.version,
mfCode: this.filters.mfCode == "" ? null : this.filters.mfCode,
httpCode: httpCode,
algCode: this.filters.algCode == "" ? null : this.filters.algCode,
httpName: httpName,
source: this.filters.source,
pageNum: 1,
pageSize: this.pageResult.totalRow
};
this.$api.httpinfo
.findPage(para, { HEADMECNODE: "HEADMECNODE" })
.then((res) => {
if (res.resultCd == 0) {
let data = res.res.data;
if (data.length == 0) {
this.exportLoading = false;
this.$message({ message: "数据为空,导出失败", type: "error" });
return;
}
//2、导出excel文件
this.$api.httpexcel
.pmExport(JSON.stringify(data), { HEADMECNODE: "HEADMECNODE" })
.then((res) => {
this.exportLoading = false;
//responseType为arryBuffer,成功返回的是文件流,失败为json,则成功会转化json异常
let json = null;
try {
var enc = new TextDecoder("utf-8");
json = JSON.parse(enc.decode(new Uint8Array(res))); //转化成json对象
if (json.resultCd === -1) {
this.$message({
message: "导出失败," + json.msg,
type: "error",
});
}
} catch (e) {
//成功返回时会解析异常
var a = document.createElement("a");
// 字符内容转变成blob地址
var blob = new Blob([res], {
type: "application/vnd.ms-excel;charset=utf-8",
});
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "北向接口配置信息.xlsx";
a.click();
window.URL.revokeObjectURL(url);
}
});
} else {
this.exportLoading = false;
this.$message({ message: "导出失败," + res.msg, type: "error" });
}
});
},
//导入Excel文件
importExcel(content) {
this.importLoading = true;
var form = new FormData(); // FormData 对象
form.append("file", content.file); // 文件对象
this.$api.httpexcel
.pmImport(form, { HEADMECNODE: "HEADMECNODE" })
.then((res) => {
this.importLoading = false;
if (res.resultCd == 0) {
this.$message({ message: "导入成功", type: "success" });
//刷新
this.findPage(null);
} else {
this.$message({ message: "导入失败," + res.msg, type: "error" });
}
//清空上传文件
this.$refs.upload.clearFiles();
});
},
//自定义组件按钮触发el-upload的选择文件框
triggerUpload() {
this.$refs.uploadBtn.$el.click();
},
java代码见代码文件
后端
深入理解Java并发之synchronized实现原理
【Java 8 新特性】如何将Java流转化成数组
Optional 类
toString()与new String ()用法区别
定义一个经过Base64加密的字符串
String str=“TU0jV0xBTiNVYys5bEdiUjZlNU45aHJ0bTdDQStBPT0jNjQ2NDY1Njk4IzM5OTkwMDAwMzAwMA==”
现在解密:
=
String rlt1=new String( Base64.decode(str, Base64.DEFAULT));
String rlt2=Base64.decode(str, Base64.DEFAULT).toString();
结果是:
rlt1=“MM#WLAN#Uc+9lGbR6e5N9hrtm7CA+A==#646465698#399900003000”
rlt2="[B@41547740"
哪一个是正确的?为什么?
这里应该用new String()的方法,因为Base64加解密是一种转换编码格式的原理
toString()与new String ()用法区别
str.toString是调用了b这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]。
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
什么时候用什么方法呢?
new String()一般使用字符转码的时候,byte[]数组的时候
toString()将对象打印的时候使用
Stream流集合
情况:只取数据里面的特定key组成数组
代码
//节点名称list
String[] op = avail.stream().filter(
x -> x.getInteger("num") > 0
).map(jsonObject -> jsonObject.getString("name")).toArray(String[]::new);
//增加显卡版本筛选
YunNode yunNode = yunNodeService.findByNodes(op, yun.getId(),containerVo.getVersionType());
if (yunNode==null){
lock.unlock(yun.getId());
return new GlobalResponse(false, "找不到节点信息");
}
YunImgConfig yunImgConfig = yunImgConfigService.findYunImgConfigById(containerVo.getImgConfigId());
map排序
List<IvmFace> resJson = ivmFaceList.stream().map(face -> {
String score = res.stream().filter(obj -> obj.getString("faceId").equals(face.getIvuFaceId())).findFirst().get().getString("score");
IvmFace indexJson = new IvmFace();
BigDecimal b = new BigDecimal(Float.parseFloat(score));
float m_score = b.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
indexJson.setScore(m_score);
indexJson.setOtherPara(face.getOtherPara());
indexJson.setCreateTime(face.getCreateTime());
indexJson.setFaceImageId(face.getId());
indexJson.setName(face.getName());
indexJson.setRepositoryId(face.getRepoId());
indexJson.setPersonId(face.getCredentialNumber());
indexJson.setFaceImageUrl(Common.IMG_PATH + face.getId() + ".jpg");
return indexJson;
}).sorted(Comparator.comparing(IvmFace::getScore).reversed()).collect(Collectors.toList());
MVC框架:
M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。
框架(framework):是一个框子——指其约束性,也是一个架子——指其支撑性。
JAVA常见设计模式
Spring框架
Springboot
初闻SpringBoot的开发者,必然会想到Spring这一时下最流行的开发框架,SpringBoot确实和Spring有着千丝万缕的关系。要想将SpringBoot学习透彻,对于Spring的学习也是必不可少的(不对Spring作过多介绍)。随着动态语言的流行(Ruby、Groovy、Scala、Node.js),Java的开发显得也就显得格外的笨重,即使是使用各种流行框架(Spring等),依然会有各种繁重的配置,导致了低效率的开发、复杂的部署流程以及第三方技术集成难度大。为了提升开发效率,节约开发成本,SpringBoot也就应用而生。它使用习惯优于配置的理念,让开发者无需繁重、复杂的文件配置就可以快速地将项目运行起来。
使用SpringBoot很容易创建一个独立运行(运行jar,内嵌Servlet)、准生产级别的基于Spring框架的项目,它可以不用或者说只需要使用很少的Spring配置。使用SpringBoot能为我们带来什么简单的概括起来就是简单、快速、配置少比起传统的Spring Web项目,
它不需要下列如此多的步骤:1. 配置web.xml,springmvc.xml和spring.xml 2. 配置数据库连接池,配置数据库事务等 3. 配置记录系统工作的日志 4. 配置加载系统运行时系统配置文件的读取… 5. 代码编写完成后,需要部署到tomcat等运行环境上调试 6. 不支持持续集成、持续部署等
SSH与Spring
⚠️⚠️概念区分
SSH框架一般指的是Struts、Spring、Hibernate,后来Struts2代替了Struts。最近5年,Struts2已经被Spring MVC代替,而Hibernate基本也被iBatis/MyBatis代替。所以你真正需要了解的是Spring,Spring你需要了解两个概念AOP和IOC,更进一步就是Spring的核心技术“动态代理”。持久层框架看看Hibernate/iBatis/MyBatis,研究一下Spring的数据库事务管理,各个持久层框架式如何实现ORM的,了解至少一种缓存框架,知道什么是数据库连接池。和数据库的交互最核心的不是ORM,而是掌握和数据库相关的能力,比如数据库编程、SQL优化、对范式的理解。MVC框架Struts2和Spring MVC,你需要知道他们在web.xml文件里配置了一些什么,Struts2/Spring MVC接到前台请求之后经过哪些步骤之后转到Action/Controller,return之后Struts2/Spring MVC又作了些什么。还有就是Filter、Listener和Struts2/Spring MVC的拦截器各有什么特点,适合处理什么样的问题。(转载自知乎)
原答案来源
mybatis
其中userids是String[] 型:
<if test="userIds!=null">
and dev.user_id in
<foreach collection="userIds" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
另一种写法:
<foreach collection="users.split(',')" item="item" index="index" open="(" close=")" separator=",">
'${item}'
</foreach>
大于小于写法:
<![CDATA[ <= ]]>
<![CDATA[ >= ]]>
相似:
LIKE CONCAT('%',#{username},'%')
分组搭配:
select GROUP_CONCAT(a.user_id) as users
group by ...
⚠️ mybatis trim 用法
⚠️ mybatis when then 用法
⚠️⚠️ mybatis批量操作
⚠️ ⚠️⚠️mybatis各种标签用法
创建临时表:
Long createTemp(@Param(“tableName”) String tableName);
<update id="createTemp">
CREATE TABLE iva_feature.${tableName}
(group_id varchar(100) PRIMARY key,
tag varchar(100),
engine_version varchar(45),
is_repo tinyint(2),
url varchar(200)
)
</update>
插入:
<insert id="insertTemp">
insert INTO iva_feature.${tableName}(group_id, tag, engine_version, is_repo,url)
select id as group_id,name as tag, #{version}, 1, #{url} from iva_alg.alg_ivm_repository where
engine_id= #{engineId}
</insert>
微服务类
Consul
Kafka
数据库类
mysql
⚠️mysql语句大全1
⚠️mysql语句大全2
⚠️mysql执行顺序
⚠️mysql JOIN图