主要介绍一些小技巧之类,是为备忘也。
TypeScript 导入 *.vue 报错:Cannot find module
如图
创建一个 shims.d.ts 文件,放置到 src/globalDeclare
中。
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
typescript-eslint 自作多情提示 xxx is assigned a value but never used
eslintrc.js 加上
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": ["off"]
ViewUI Table 单元格 文本将不换行,超出部分显示为省略号
组件写法,比较麻烦
<Table :columns="columns1" :data="list">
<template slot-scope="{ row, index }" slot="action">
<a href="javascript:void(0);" @click="handleEdit(row, index)">编辑</a>
<Divider type="vertical" />
<Poptip confirm transfer title="是否要删除此行?" @on-ok="handleDelete(index)">
<a href="javascript:void(0);" style="color:red;">删除</a>
</Poptip>
</template>
<template slot-scope="{ row }" slot="url">
<Ellipsis :text="row.url" :length="50" tooltip :transfer="true"></Ellipsis>
</template>
</Table>
其实可以在列配置中声明:
{ title: '链接地址', minWidth: 190, key: 'url', ellipsis:true, tooltip:true },
另外每一列设置 width/minWidth
就可以保证不受浏览器宽度挤压
Vue 工程里面怎么引入公共的 Less 样式库?
例如 Less 的函数。
安装下面插件
- “less”: “^3.0.4”,
- “less-loader”: “^5.0.0”,
- “style-resources-loader”: “^1.4.1”
打开 vue.config.js 配置文件,
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: ['C:\\code\\ajaxjs\\aj-js\\aj-ui\\src\\style\\common-functions.less']
}
},
lintOnSave: true,
devServer: {
overlay: {
warnings: true,
error: true
}
}
};
路径写死,改相对路径
var path = require("path");
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, './src/style/common-functions.less')]
}
},
lintOnSave: true,
devServer: {
overlay: {
warnings: true,
error: true
}
}
};
MySQL varchar 文本包含数字的计数器
需求:如果重复值,则自增 1、2、3……
思路:先查询是否重复:
SELECT id FROM ${tableName} WHERE urlDir = ? AND datasourceId = ? LIMIT 1
如果是,获取最大值 MaxId
,通过正则查询、排序,注意参数拼接了字符串(参数就是重复值)。
SELECT urlDir FROM ${tableName} WHERE urlDir REGEXP CONCAT(?, '_[0-9]+$') AND datasourceId = ? ORDER BY urlDir DESC LIMIT 1
若无则 1,有则 MaxId++
。
快速 SQL 转换 Java Bean/ POJO
找过好几个的,都不太符合需求,于是自己写个脚本,也很快。
<html>
<head>
<meta charset="utf-8" />
<title>SQL2pojo</title>
</head>
<body>
<textarea id="sql" rows="20" cols="100"></textarea>
<br />
<br />
<button onclick="sql2pojo()">SQL2pojo</button>
<pre></pre>
</body>
<script>
let tpl = '';
function sql2pojo() {
let sql = document.querySelector("#sql").value;
let arr = sql.match(/CREATE TABLE `(?:\w+|_)` \(((\s|\S)+)(?=PRIMARY KEY)/);
let result = arr[1].trim();
arr = result.split(',');
let output = [];
arr.forEach(item => {
if (item) {
item = item.trim();
console.log(item);
let _arr = item.match(/^`(\w+)`\s+((?:\w|\(|\))+).*COMMENT '(.*?)'/);
let _t = _arr[2], type = 'Object';
if (_t.indexOf('VARCHAR') != -1 || _t.indexOf('TEXT') != -1)
type = 'String';
if (_t.indexOf('TINYINT(1)') != -1)
type = 'Boolean';
else if (_t.indexOf('TINYINT') != -1)
type = 'Integer';
else if (_t.indexOf('INT') != -1)
type = 'Long';
if (_t.indexOf('DATETIME') != -1 || _t.indexOf('DATE') != -1)
type = 'Date';
tpl = `
/**
* ${_arr[3]}
*/
private ${type} ${_arr[1]};`;
// console.log(tpl);
output.push(tpl);
}
});
document.querySelector('pre').innerHTML = output.join('<br />');
}
</script>
</html>
Vue+TS 工程发布 npm 组件不能携带 *.vue 问题
当前 Vue 工程既有网站,也希望发布为 npm 组件。使用 tsc
编译结果到 dist 目录,注意下面问题:
- 配置 main 文件,不然
import
包时候会undefined
。具体就是在 package.json 配置结果目录的index.js
和index.d.ts
。
- 编译依靠不能使用
vue-cli-service build
,那是编译网站的。我们目的是打包组件,使用tsc
即可。其实就是生成 js 和 map,我的tsconfig.json
配置如下。
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": false,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"outDir": "./dist",
"types": ["webpack-env"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": ["node_modules", "dist"]
}
其中的 include
"src/**/*.vue",
其实没作用,因为 tsc
只管 js/ts/json 的编译,其他文件它不处理的。那么问题来了——打包就需要 *.vue
文件,——我搜索了很久终于找到一个比较简单的方法,就是直接复制过去。package.json
增加一个 scripts
命令:
"release": "tsc && xcopy src\\components dist\\components /s /y /d && npm publish --access public",
tsc 编译后通过 DOS 命令 xcopy
复制目录,/s
表示包含所有子目录和文件, /y
表示不确认并进行覆盖,/d 表示文件日期对比,日期较新的不覆盖。
另外,tsc 不会覆盖现有文件,所以最好先删除一下 dist 目录再 npm run release
。
Vue 中单页面组件中 render 函数不运行?
要用 render 函数,把 <template>
给去掉。https://segmentfault.com/q/1010000016677825
简单使得元素可拖动
/**
* 使得面板浮动,可拖放
*/
export default function float() {
setTimeout(() => {
let el: HTMLElement = this.$el;
let rect: DOMRect = el.getBoundingClientRect();
let controls: HTMLElement = (<HTMLElement>el.querySelector('.controls'));
let top: number = rect.top - el.offsetTop;
let left: number = rect.left - el.offsetLeft - controls.offsetWidth - 10;
let style: CSSStyleDeclaration = controls.style;
style.top = top + 'px';
style.left = left + 'px';
makeDD(controls, <HTMLElement>controls.querySelector('.movable'));
let btns: HTMLElement = (<HTMLElement>el.querySelector('.btns'));
top = rect.top - el.offsetTop - btns.offsetHeight - 10;
left = rect.left - el.offsetLeft;
style = btns.style;
style.top = top + 'px';
style.left = left + 'px';
makeDD(btns, <HTMLElement>btns.querySelector('.movable'));
}, 10);
}
/**
* 拖放
*
* @param box 被拖放的区域
* @param dragBar 拖放的按钮
*/
function makeDD(box: HTMLElement, dragBar: HTMLElement): void {
// 鼠标按下的函数
dragBar.onmousedown = function (oEvent: MouseEvent) {
// 求出鼠标和box的位置差值
let x: number = oEvent.clientX - box.offsetLeft, y: number = oEvent.clientY - box.offsetTop;
// 鼠标移动的函数
// 把事件加在document上,解决因为鼠标移动太快时,鼠标超过box后就没有了拖拽的效果的问题
document.onmousemove = function (oEvent: MouseEvent) {
// 只能拖动窗口标题才能移动
if (oEvent.target != dragBar) {
// return;
}
// 保证拖拽框一直保持在浏览器窗口内部,不能被拖出的浏览器窗口的范围
let l: number = oEvent.clientX - x, t = oEvent.clientY - y;
let doc: HTMLElement = document.documentElement;
if (l < 0)
l = 0;
else if (l > doc.clientWidth - box.offsetWidth)
l = doc.clientWidth - box.offsetWidth;
if (t < 0)
t = 0;
else if (t > doc.clientHeight - box.offsetHeight)
t = doc.clientHeight - box.offsetHeight;
box.style.left = l + "px";
box.style.top = t + "px";
}
// 鼠标抬起的函数
document.onmouseup = function () {
document.onmousemove = document.onmouseup = null;
}
// 火狐浏览器在拖拽空div时会出现 bug return false阻止默认事件,解决火狐的bug
return false;
}
}
Spring MVC 加入 JSP 支持
/WEB-INF/jsp/ .jsp解决烦人的 sockjs-node/info 跨域问题
打开 build/webpack.conf.js
const config = {
resolve: {
alias: {
}
},
devServer: {
// host: 'localhost',
disableHostCheck: true,
public: '0.0.0.0'
},
};
module.exports = config;
JSP 页面异常
JSP 需要加上下面代码,运行时才不会出现 java.lang.IllegalStateException: getOutputStream() has already been called
…等异常。
/**
*
* @param ctx 页面上下文
*/
public static void fix(PageContext ctx) {
HttpServletResponse response = (HttpServletResponse) ctx.getResponse();
try {
OutputStream out = response.getOutputStream();
out.flush();
out.close();
response.flushBuffer();
ctx.getOut().clear();
ctx.pushBody();
// out = pageContext.pushBody();
} catch (IOException e) {
LOGGER.warning(e);
}
}
参考 JSP 内置对象 out 和 response.getWrite()
的区别
- http://blog.sina.com.cn/s/blog_7217e4320101l8gq.html
- http://www.2cto.com/kf/201109/103284.html
响应禁止缓存
/**
* 新的输出,不要缓存
*
* @return 当前对象
*/
public MvcOutput noCache() {
setHeader("Pragma", "No-cache");
setHeader("Cache-Control", "no-cache");
setDateHeader("Expires", 0);
return this;
}
返回到前一页并刷新
window.location = document.referrer;
静态的错误提示页
<title>操作错误</title>
<meta charset="utf-8" />
<div style="height: 100%%; display: flex; justify-content: center; align-items: center;">
<table>
<tr>
<td align="center"> <svg width="150px" viewBox="0 0 1000 1000">
<g>
<path fill="#ea8010" d="M500,10c-46.7,0-84.5,38-84.5,84.9v573.7c0,46.9,37.8,84.9,84.5,84.9c46.7,0,84.5-38,84.5-84.9V94.9C584.5,48,546.7,10,500,10z M500,821c-46.7,0-84.5,37.8-84.5,84.5c0,46.7,37.8,84.5,84.5,84.5c46.7,0,84.5-37.8,84.5-84.5C584.4,858.9,546.6,821,500,821z" />
</g>
</svg></td>
</tr>
<tr>
<td align="center"><br />%s<br /><a href="javascript:history.go(-1);">返回</a></td>
</tr>
</table>
</div>
随时随地获取 Request/Response
为获取请求的上下文,能够在控制器中拿到最常用的对象,例如 HttpServletRequest
和 HttpServletResponse
等的对象(甚至 Web App 的启动上下文( 在 web.xml
中配置的参数)),因此还需要设计一个 RequestHelper
类,通过 ThreadLocal 让控制器能轻易地访问到这些对象。
一个容器,向这个容器存储的对象,在当前线程范围内都可以取得出来,向 ThreadLocal 里面存东西就是向它里面的 Map
存东西的,然后 ThreadLocal 把这个 Map 挂到当前的线程底下,这样 Map 就只属于这个线程了。
private static ThreadLocal<HttpServletRequest> threadLocalRequest = new ThreadLocal<>();
private static ThreadLocal<HttpServletResponse> threadLocalResponse = new ThreadLocal<>();
/**
* 保存一个 request 对象
*
* @param req 请求对象
*/
public static void setHttpServletRequest(HttpServletRequest req) {
threadLocalRequest.set(req);
}
/**
* 获取请求对象
*
* @return 请求对象
*/
public static HttpServletRequest getHttpServletRequest() {
return threadLocalRequest.get();
}
/**
* 保存一个 response 对象
*
* @param resp 响应对象
*/
public static void setHttpServletResponse(HttpServletResponse resp) {
threadLocalResponse.set(resp);
}
/**
* 获取上下文中 response 对象
*
* @return response 响应对象
*/
public static HttpServletResponse getHttpServletResponse() {
HttpServletResponse resp = threadLocalResponse.get();
if (resp == null)
throw new RuntimeException("响应对象未初始化");
return resp;
}
/**
* 清空 request 和 response
*/
public static void clean() {
threadLocalRequest.set(null);
threadLocalResponse.set(null);
}
javax.servlet.forward.request_uri 之作用
绑定 JSP 时候,获取原请求的 uri,而非模版所在的 uri。
生成模拟数据的 SQL
随机 SET
UPDATE enterprise_tract_meeting SET TYPE = ELT(FLOOR(RAND() * 3 + 1), 1, 2, 3);
IDEA 设置 Javac 编译参数对于 Maven 无效
Eclipse 不会那样,Idea 2018 无效。你 re-build 然后 deploy 那样就可以。下面的 pom.xml
永久解决。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg><!-- IDEA 设置 Javac 编译参数对于 Maven 无效 -->
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
Mysql 创建流水号
方法一,触发器:
CREATE TRIGGER saledetail_id BEFORE INSERT ON saledetail
FOR EACH ROW BEGIN
declare n int;
select IFNULL(max(right(ItemID,4)),0) into n from saledetail where mid(ItemID,1,8)=DATE_FORMAT(CURDATE(),'%Y%m%d');
set NEW.ItemID=concat(DATE_FORMAT(CURDATE(),'%Y%m%d'),right(10001+n,4));
END;
注意在插入的时候主键要设置一个默认值才能插入进去,这里我设置的是空字符串 ""
。
方法二:
SELECT
substr(CONCAT('0000', (IFNULL(MAX(substr(fund_code, -3)),0) + 1)), -3)
FROM enterprise_trace_fund
WHERE fund_code LIKE 'DF20211223%'
fund_code
字段值就是传过来的字符串,mysql 数据库会自动进行匹配,然后自行自增。fund_code
字段值如果加入日期值,三位的流水号一般是够用的。
但这做法并不能在并发下保证流水号的唯一性。可以用 MySQL 写锁(select...for update
,也叫 X 锁,排它锁)。
方法三:
CREATE DEFINER=`root`@`localhost` PROCEDURE `GetSerialNo`(IN tsCode VARCHAR(50),OUT result VARCHAR(200) )
BEGIN
DECLARE tsValue VARCHAR(50);
DECLARE tdToday VARCHAR(20);
DECLARE nowdate VARCHAR(20);
DECLARE tsQZ VARCHAR(50);
DECLARE t_error INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;
START TRANSACTION;
/* UPDATE sys_sno SET sValue=sValue WHERE sCode=tsCode; */
SELECT sValue INTO tsValue FROM sys_sno WHERE sCode=tsCode for UPDATE;
SELECT sQz INTO tsQZ FROM sys_sno WHERE sCode=tsCode ;
-- 因子表中没有记录,插入初始值
IF tsValue IS NULL THEN
SELECT CONCAT(DATE_FORMAT(NOW(),'%y%m'),'0001') INTO tsValue;
UPDATE sys_sno SET sValue=tsValue WHERE sCode=tsCode ;
SELECT CONCAT(tsQZ,tsValue) INTO result;
ELSE
SELECT SUBSTRING(tsValue,1,4) INTO tdToday;
SELECT CONVERT(DATE_FORMAT(NOW(),'%y%m'),SIGNED) INTO nowdate;
-- 判断年月是否需要更新
IF tdToday = nowdate THEN
SET tsValue=CONVERT(tsValue,SIGNED) + 1;
ELSE
SELECT CONCAT(DATE_FORMAT(NOW(),'%y%m') ,'0001') INTO tsValue ;
END IF;
UPDATE sys_sno SET sValue =tsValue WHERE sCode=tsCode;
SELECT CONCAT(tsQZ,tsValue) INTO result;
END IF;
IF t_error =1 THEN
ROLLBACK;
SET result = 'Error';
ELSE
COMMIT;
END IF;
SELECT result ;
END;
出处:https://www.jianshu.com/p/d7570564f104
Eclipse Maven Dependencies下引入本地工程的 jar 包却变成源码文件夹
How to tell Maven to include the jar dependency, not the subproject source directory in Eclipse?
开始找到这个方法 https://blog.csdn.net/Harbourside1/article/details/111871122 是不对的,后来找到这个 https://blog.csdn.net/oh_maxy/article/details/48347897,正确!就是没有选 Utility Module 这个导致的:
Spring MVC Java 代替 xml
web.xml
原来 web.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<!-- 启用 Spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- // -->
<!-- 对 Request、Response 的扩展 -->
<filter>
<filter-name>InitMvcRequest</filter-name>
<filter-class>com.ajaxjs.util.spring.InitMvcRequest</filter-class>
</filter>
<filter-mapping>
<filter-name>InitMvcRequest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 全部允许跨域 -->
<filter>
<filter-name>Cors</filter-name>
<filter-class>com.ajaxjs.util.spring.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- // -->
</web-app>
<!-- // -->
采用 Java:
public abstract void initWeb(ServletContext servletContext);
@Override
public void onStartup(ServletContext servletCxt) {
LOGGER.info("WEB 程序启动中……");
servletCxt.setInitParameter("contextConfigLocation", "classpath:applicationContext.xml");
servletCxt.addListener(new ContextLoaderListener()); // 监听器
FilterRegistration.Dynamic filterReg = servletCxt.addFilter("InitMvcRequest", new InitMvcRequest());
filterReg.addMappingForUrlPatterns(null, true, "/*");
initWeb(servletCxt);
……
}
数据库连接池失效
注意 Connection 一定要关闭!
<!-- 配置数据源 https://blog.csdn.net/syslbjjly/article/details/97108560 -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<!--内网数据库 -->
<property name="url" value="jdbc:mysql://10.201.xxx.xxx:3306/bdp?useUnicode=true&characterEncoding=UTF-8&useSSL=false"></property> <property
name="username" value="root"></property> <property name="password" value="xxx"></property>
<!-- 验证连接是否有效,(String) SQL 查询,用来验证从连接池取出的连接,在将连接返回给调用者之前。 如果指定,则查询必须是一个 SQL SELECT 并且必须返回至少一行记录查询不必返回记录,但这样将不能抛出 SQL 异常 -->
<property name="validationQuery" value="SELECT 1" />
<!-- (long) 避免过度验证,保证验证不超过这个频率——以毫秒为单位。如果一个连接应该被验证, 但上次验证未达到指定间隔,将不再次验证。 30000(30秒) -->
<property name="validationInterval" value="18800" />
<!-- 验证失败时,是否将连接从池中丢弃 -->
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
</bean>
MVC 框架中静态资源的划分
一般都是 MVC 框架接收所有的请求然后分别处理,去控制器的,还是静态资源的,
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* 自定义请求对象,对请求对象和响应对象有很多需要扩展的地方
*
* @author Frank Cheung<sp42@qq.com>
*
*/
//@Component
public class InitMvcRequest implements Filter {
/**
* 字符串判断是否静态文件
*/
private static final Pattern IS_STATIC = Pattern.compile("\\.jpg|\\.png|\\.gif|\\.js|\\.css|\\.less|\\.ico|\\.jpeg|\\.htm|\\.swf|\\.txt|\\.mp4|\\.flv");
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest _req = (HttpServletRequest) req;
try {
// 为防止中文乱码,统一设置 UTF-8,设置请求编码方式
_req.setCharacterEncoding(StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
}
if (!IS_STATIC.matcher(_req.getRequestURI()).find())
chain.doFilter(req, resp);
else
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig arg0) {
}
@Override
public void destroy() {
}
}
关于 Tomcat 的一些冷知识
Tomcat 自带许多有用的组件,直接可用。
- Tomcat 也有自己的数据库连接池 jdbc-pool
- 自带管理监控工具 Manager,若不满可以参考 PSI Probe
- 想要一个模板系统?用 Tomcat 自带的 EL表达式解析器 吧,可惜的是我找不到相关的教程……只能用 Spring 的
- 可插拔以及 SCI 的实现原理,以及 Wrapper
- 对于特定资源的保护,Tomcat 提供了安全域的功能实现
- Tomcat 也可以做 SSO……
- 一堆过滤器
我们知道 Spring 本身自带一堆过滤器,Tomcat 也有呀。
SetCharacterEncodingFilter
解决乱码问题CorsFilter
跨域问题CsrfPreventionFilter
防止跨站请求伪造(CSRF)RemoteIpFilter/RemoteIpValve
获取客户端真实 ipRemoteHostFilter
、RemoteAddrFilter
获取客户端Host、Ip
观察代码执行时间
分析效率用,用 Spring 的 StopWatch
。
StopWatch sw = new StopWatch();
sw.start("起床");
Thread.sleep(1000);
sw.stop();
sw.start("洗漱");
Thread.sleep(2000);
sw.stop();
System.out.println(sw.prettyPrint());
System.out.println(sw.getTotalTimeMillis());
System.out.println(sw.getLastTaskName());
System.out.println(sw.getLastTaskInfo());
System.out.println(sw.getTaskCount());
如何对Spring MVC中的 Controller 进行单元测试
例子如下,参见。
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestAlipay {
MockMvc mockMvc;
@Autowired
WebApplicationContext wac;
@Before
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// @Test
public void testCommonUpload() throws Exception {
File file = new File("C:\\Users\\frank\\Desktop\\abldj75zav.png");
byte[] bytes = FileHelper.openAsByte(file);
String filenmae = "abldj75zav.png";
MockMultipartFile mockMultipartFile = new MockMultipartFile("file", filenmae, MediaType.MULTIPART_FORM_DATA_VALUE, bytes);
ResultActions andDo = mockMvc.perform(multipart("/upload").file(mockMultipartFile)).andExpect(status().isOk()).andExpect(content().string(filenmae)).andDo(print());
System.out.println(andDo);
assertNotNull(andDo);
}
@Test
public void testNso() {
assertTrue(true);
}
}
MySQL 于 Tomcat 的冲突
出现异常:checkStateForResourceLoading Illegal access
,Eclipse 提示异常,生产环境应该不会。写一个 Listener 解决。出处:1、2。
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.stereotype.Component;
import com.ajaxjs.Version;
import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
/**
* Eclipse 提示异常,生产环境应该不会
*
* @author Frank Cheung<sp42@qq.com>
*
*/
@WebListener
@Component
public class ContainerContextClosedHandler implements ServletContextListener {
@SuppressWarnings("deprecation")
@Override
public void contextDestroyed(ServletContextEvent arg0) {
if (Version.isDebug) {
Enumeration<Driver> drivers = DriverManager.getDrivers();
Driver driver = null;
// clear drivers
while (drivers.hasMoreElements()) {
try {
driver = drivers.nextElement();
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
// deregistration failed, might want to do something, log at the very least
}
}
// MySQL driver leaves around a thread. This static method cleans it up.
try {
AbandonedConnectionCleanupThread.shutdown();
} catch (Exception e) {
// again failure, not much you can do
}
}
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("------------------------------------------");
}
}
错误信息 HTML
急用一个错误提示页面:
<title>操作错误</title>
<meta charset="utf-8" />
<div style="height: 100%%; display: flex; justify-content: center; align-items: center;">
<table>
<tr>
<td align="center"> <svg width="150px" viewBox="0 0 1000 1000">
<g>
<path fill="#ea8010" d="M500,10c-46.7,0-84.5,38-84.5,84.9v573.7c0,46.9,37.8,84.9,84.5,84.9c46.7,0,84.5-38,84.5-84.9V94.9C584.5,48,546.7,10,500,10z M500,821c-46.7,0-84.5,37.8-84.5,84.5c0,46.7,37.8,84.5,84.5,84.5c46.7,0,84.5-37.8,84.5-84.5C584.4,858.9,546.6,821,500,821z" />
</g>
</svg></td>
</tr>
<tr>
<td align="center"><br />错误XXXX<br /><a href="javascript:history.go(-1);">返回</a></td>
</tr>
</table>
</div>
效果如图
Eclipse 重启 Tomcat 提示 May be locked by another process
每次重启都会,很烦。原因是有打开文件的文件未关闭,Files.lines()
打开返回的 Stream<String>
也要关闭!
StringBuilder sb = new StringBuilder();
try (Stream<String> lines = Files.lines(path, encode);) {
lines.forEach(str -> sb.append(str));
return sb.toString();
} catch (IOException e) {
LOGGER.warning(e);
}
典型的 Spring MVC 控制器单测
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.ajaxjs.data_service.api.ApiController;
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestSsoAccessTokenInterceptor {
MockMvc mockMvc;
@Autowired
WebApplicationContext wac;
@Autowired
ApiController apiController;
@Before
public void init() {
apiController.initCache();
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void test() throws Exception {
MockHttpServletRequestBuilder req = get("/user_api").param("redirect_uri", "https://www.qq.com").param("client_id", "dss23s");
mockMvc.perform(req).andExpect(status().is2xxSuccessful()).andDo(print()).andReturn().getResponse();
}
}
规避使用 PowerDesigner
老版本 12 没有上传数据的功能,可以规避版权检测。通用破解方法:修改安装目录下的 pdflm12.dll 文件,使用二进制编辑器打开此文件,查找:83 C4 14 8B 85 E4 FE FF FF
将此字符串改为 83 C4 14 33 C0 90 90 90 90
推荐 Hex Editor: wxMEdit。 Frhed 也小巧,但找不到搜索 Hex 的方法,郁闷。
使用正则提取网页中a标签的链接和标题
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test1 {
public static void main(String[] args) {
String str1 = "<a href=\"https://www.zifangsky.cn/2015/10/hello-world/\" title=\"\" data-original-title=\"Hello World\">Hello World</a>";
String str2 = "<a href=\"http://banzhuanboy.com/363.html\" class=\"post-feature\" \">123</a>";
String str3 = " <a class=\"article-title\" href=\"/2015/12/17/Webstorm-Hotkeys-For-Mac/\">c</a>";
String str4 = " <a rel=\"bookmark\" title=\"Permanent Link to 黑客组织‘SkidNP’涂改了Phantom Squad的网站首页\" href='12/hack-30127.htm'>黑</a>";
String str5 = "<a href=\"http://www.imorlin.com/2015/12/24/1-3/\" title=\"\" data-original-title=\"2015圣诞节雪花代码[天猫+C店]\"> 2015圣诞节雪花代码[天猫+C店] <span class=\"label label-new entry-tag\">New</span> </a>";
Pattern pattern = Pattern.compile("<a.*?href=[\"']?((https?://)?/?[^\"']+)[\"']?.*?>(.+)</a>");
Matcher matcher = pattern.matcher(str1);
if(matcher.find()){
String link = matcher.group(1).trim();
String title = matcher.group(3).trim();
if(!link.startsWith("http")){
if(link.startsWith("/"))
link = "https://www.zifangsky.cn" + link;
else
link = "https://www.zifangsky.cn" + link;
}
System.out.println("link: " + link);
System.out.println("title: " + title);
}
}
}
解释:
1 选取了几个有代表性的 a 标签样式进行测试
2 关于正则匹配模式”<a.*?href=[\”‘]?((https?://)?/?[^\”‘]+)[\”‘]?.*?>(.+)</a>
“的说明:
i)<a.*?href=
<a
开头,中间紧跟着有0个或者多个字符,然后再跟着 href=
ii)[\”‘]?((https?://)?/?
一个或者0个的”
或者 ‘
,然后再跟着0个或者一个的http://
或者https://
,再跟着0个或者1个的 /
iii)[^\”‘]+
表示1个以上的不包括’
或者”
的任意字符
iv)[\”‘]?
表示链接后面的’
或者”
当然也可能没有
后面的可以根据前面的自己推理,就不解释了
3 matcher.group(1)
表示取出链接,也就是第二个()
的内容(PS:第一个()
表示的是整个正则表达式,默认省略了)
,在正则中是这一段规则:((https?://)?/?[^\”‘]+)
4 matcher.group(3)
同理可知,对应的是这一段规则:(.+)
5 对于代码中的 https://www.zifangsky.cn
,这是由于部分链接使用了相对路径,比如说:href=’12/hack-30127.htm’
。这时我们就需要加上它的域名,当然需要根据实际情况来加。这里我就随便乱加了
构建可重复读取 inputStream 的 request
我们知道,request 的 inputStream
只能被读取一次,多次读取将报错,那么如何才能重复读取呢?答案之一是:增加缓冲,记录已读取的内容。
import org.springframework.mock.web.DelegatingServletInputStream;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* request wrapper: 可重复读取request.getInputStream
*/
public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
private static final int BUFFER_START_POSITION = 0;
private static final int CHAR_BUFFER_LENGTH = 1024;
/**
* input stream 的buffer
*/
private final String body;
/**
* @param request {@link javax.servlet.http.HttpServletRequest} object.
*/
public RepeatedlyReadRequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
} catch (IOException e) {
log.error("Error reading the request body…", e);
}
if (inputStream != null) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
char[] charBuffer = new char[CHAR_BUFFER_LENGTH];
int bytesRead;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, BUFFER_START_POSITION, bytesRead);
}
} catch (IOException e) {
log.error("Fail to read input stream",e);
}
} else {
stringBuilder.append("");
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new DelegatingServletInputStream(byteArrayInputStream);
}
}
接下来,需要一个对应的 Filter
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class RepeatlyReadFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
request = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
@Override
public void destroy() { }
}
出处。
使用 Javassist 在 tomcat 容器中实现动态 Mock
在某些复杂场景下,我们需要对运行在 tomcat 容器中部分功能进行 mock(替换其实现),但该部分功能散落在各处,我们希望不修改源代码以非侵入的方式来实现 Mock,在这种情况下,我们可以应用 Javassist 来实现。
使用Javassist在tomcat容器中动态替换源码来实现动态 Mock
我们可以定义一个 ContextListener 的实例,在 tomcat 启动时通过 Javassis t对源代码进行动态替换,来实现 mock 的功能。
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.HashMap;
import java.util.Map;
import org.lightfw.utilx.dynamic.JavassistUtil;
public class MockListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent contextEvent) {
log.info("Context Listener start................");
//将TestController类的test方法的实现替换为"return 1;"
JavassitUtil.replaceMethodBody("com.xx.web.controller.TestController", "test", "return 1");
log.info("Context Listener started");
}
public void contextDestroyed(ServletContextEvent sc) {
log.info("Context Listener stopped");
}
}
Javassist 相关代码,需要使用的工具类。
private static ClassPool classPool;
static {
classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(JavassitUtil.class)); //主要用于web环境
}
/**
* 替换方法体
*
* @param className 类名,如:foo.Student
* @param methodName 方法名
* @param newMethodBody 新的方法体,如:"System.out.println(\"this method is changed dynamically!\");"
*/
public static void replaceMethodBody(String className, String methodName, String newMethodBody) {
try {
CtClass clazz =classPool.get(className);
CtMethod method = clazz.getDeclaredMethod(methodName);
method.setBody(newMethodBody);
clazz.toClass();
} catch (NotFoundException | CannotCompileException e) {
throw new RuntimeException(e);
}
}
出处。
Spring 单元测试干掉 xml
Spring 全注解化了,但单测还是有个 xml 的小尾巴。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 扫描的包 -->
<context:component-scan
base-package="com.ajaxjs.data_service, com.ajaxjs.entity, com.ajaxjs.rpc" />
</beans>
二货 Eclipse 整天校验这个 xml,还卡住。——其实可以创建一个 Java 类来代替 XML 文件:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({ "com.ajaxjs.data_service", "com.ajaxjs.entity", "com.ajaxjs.rpc" })
public class TestConfig {
}
单测:
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.ajaxjs.entity.datadict.DataDictService;
@ContextConfiguration(classes = TestConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestDataDict {
@Autowired
DataDictService dataDictService;
@Test
public void test() {
assertNotNull(dataDictService);
}
}
Java 枚举技巧
枚举除了名称还有常量值,可以用 int 保存。
public static enum Lock {
ADD_LOCK(0), UNLOCK(1);
private int value;
Lock(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
如果常量值是 0、1、2 顺序的,可以不设置 int,直接:
public static enum Lock {
ADD_LOCK, UNLOCK;
}
lock.ordinal()
即可返回顺序的 int。
Ubuntu sudo 不用每次都输入密码的解决办法
虽然 sudo -i
可以避免输入密码,但 sftp 不行啊,怎么办!?
网上说的办法都不行,直接修改/etc/sudoers
文件的最后一行:
%sudo ALL=(ALL:ALL) ALL 修改为 %sudo ALL=(ALL:ALL) NOPASSWD:ALL
我还改崩了,无法使用 sudo,解决办法参见《Ubuntu改坏sudoers后无法使用sudo的解决办法》
实际上 如果你对那文件有用户权限,是不用输入密码的,使用 chown -R 用户名
修改就行。
前端:左菜单,右主区域,左绝对值,右自动填满的布局
用 Flex 布局,如下:
.container {
display: flex;
height: 100%;
.left {
height: 100%;
flex: 0 0 300px;
border-right: 1px solid lightgray;
}
.right {
height: 100%;
flex: 1;
/*div占据所有剩余宽度 */
}
}
JS 快速压缩 CSS 代码
很简单的:
/* 压缩 css 并保存 */
function compress(code) {
code = code.replace(/\n/ig, ''); // 去掉换行
code = code.replace(/(\s){2,}/ig, '$1'); // 多空间(两个以上) 变 一个空格
code = code.replace(/\t/ig, ''); // 去掉tab
code = code.replace(/\n\}/ig, '\}'); // 换行+} 变 不换行
code = code.replace(/\n\{\s*/ig, '\{'); // {+换行 变 不换行
code = code.replace(/(\S)\s*\}/ig, '$1\}'); // 去掉 内容 与 } 之间的空格
code = code.replace(/(\S)\s*\{/ig, '$1\{'); // 去掉 内容 与 { 之间的空格
code = code.replace(/\{\s*(\S)/ig, '\{$1'); // 去掉 { 与 内容之间空格
return code;
}
MySQL 调整时区
SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
如果是中国标准时间, 会输出08:00
修改时区
set global time_zone = '+8:00'; ##修改mysql全局时区为北京时间,即我们所在的东8区
set time_zone = '+8:00'; ##修改当前会话时区
flush privileges; #立即生效
IDEA 社区版新建 SpringBoot 项目
社区版下,Spring BootHelper 居然收费。怎么破?利用官方的 Spring Initializr 生成项目,导入即可。
Mybatis insert 的入参为map时,insert 语句中获取key和value的写法
https://blog.csdn.net/qq_40580023/article/details/84992429
MyBatis更新数据(输入参数类型为Map)
https://blog.csdn.net/Dr_Guo/article/details/79057153
Servlet 3 原生文件上传
package com.ajaxjs.image;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Collection;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import com.ajaxjs.util.io.StreamHelper;
/**
* Servlet implementation class Api
*/
@WebServlet("/img_api/*")
@MultipartConfig
public class Api extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
new Target(request);
String filename = "c:\\temp\\11.jpg";
try (InputStream bin = new BufferedInputStream(new FileInputStream(filename));) {
StreamHelper.write(bin, response.getOutputStream(), true);
}
}
String savePath = "c:\\temp\\";
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Collection<Part> parts = request.getParts();// 获取上传的文件集合
if (parts.size() == 1) {// 上传单个文件
// Servlet3.0 将 multipart/form-data 的 POST 请求封装成 Part,通过 Part 对上传的文件进行操作。
// Part part = parts[0];//从上传的文件集合中获取 Part 对象
Part part = request.getPart("file");// 通过表单 file 控件(<input type="file" name="file">)的名字直接获取 Part 对象
uplaod(part, savePath);
} else {
for (Part part : parts)// 一次性上传多个文件
uplaod(part, savePath);
}
// response.getWriter().append("Served at: ").append(request.getContextPath());
try (PrintWriter out = response.getWriter();) {
out.println("上传成功");
}
}
public static void uplaod(Part part, String savePath) {
String header = part.getHeader("content-disposition");// 获取请求头,请求头的格式:form-data; name="file"; filename="snmp4j--api.zip"
try {
part.write(savePath + File.separator + getFileName(header));// 把文件写到指定路径
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Servlet3 没有提供直接获取文件名的方法,需要从请求头中解析出来 根据请求头解析出文件名
* 请求头的格式:火狐和google浏览器下:form-data; name="file"; filename="snmp4j--api.zip"
* IE浏览器下:form-data; name="file"; filename="E:\snmp4j--api.zip"
*
* @param header 请求头
* @return 文件名
*/
public static String getFileName(String header) {
/*
* String[] tempArr1 = header.split(";");代码执行完之后,在不同的浏览器下,tempArr1数组里面的内容稍有区别
* 火狐或者google浏览器下:tempArr1={form-data,name="file",filename="snmp4j--api.zip"}
* IE浏览器下:tempArr1={form-data,name="file",filename="E:\snmp4j--api.zip"}
*/
String[] tempArr1 = header.split(";");
/*
* 火狐或者google浏览器下:tempArr2={filename,"snmp4j--api.zip"}
* IE浏览器下:tempArr2={filename,"E:\snmp4j--api.zip"}
*/
String[] tempArr2 = tempArr1[2].split("=");
// 获取文件名,兼容各种浏览器的写法
String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\") + 1).replaceAll("\"", "");
return fileName;
}
}
这样理解 java 中的 volatile 更简单些
程序运行时,有2大块内存,主内存和本地内存,当某线程读或写一个变量时,先操作本地内存,再选择合适的时机同步到主内存中。
并发三个重要的概念:原子性,可见性,有序性。
关于原子性:
1,synchronized{}
修饰的代码块,可保证原子性
2,对于volatile int i = 0;
i = 2;
是原子操作
i++;
不是原子操作,因为要先读取i的当前值,再进行自增,再进行赋值操作
i = i;
不是原子操作,因为要先读取i的当前值,再进行赋值操作
int j = i;
不是原子操作,因为要先读取i的当前值,再进行赋值操作
- volatile 只具备可见性和有序性。
- volatile 可见性,当线程给该变量赋值时,该新值会先修改到本地内存,再直接同步到主内存。
- volatile 有序性,当纯种读取该变量值时,必须先从主内存读取最新的值,再同步到本地内存中,再从本地内存中读取最新值。
Linux 启动 JAR 包 Shell 脚本
实用呀,可以 kill 掉已有进程然后启动
port=8083
pid=$(netstat -nlp | grep :$port | awk '{print $7}' | awk -F"/" '{ print $1 }')
kill -9 ${pid}
echo "killed ${pid}"
nohup java -jar auth.jar > /dev/null &
tail -f /data/logs/uam/auth/log_debug.log
最后一行 tail -f /data/logs/uam/auth/log_debug.log
观察日志的可以不执行或者修改 文档地址。
如何测试高并发的线程安全
结合 CountDownLatch类和Semaphore类测试(出处),例子如下。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author binghe
* @version 1.0.0
* @description 测试SimpleDateFormat的线程不安全问题
*/
public class SimpleDateFormatTest01 {
//执行总次数
private static final int EXECUTE_COUNT = 1000;
//同时运行的线程数量
private static final int THREAD_COUNT = 20;
//SimpleDateFormat对象
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(THREAD_COUNT);
final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < EXECUTE_COUNT; i++){
executorService.execute(() -> {
try {
semaphore.acquire();
try {
simpleDateFormat.parse("2020-01-01");
} catch (ParseException e) {
System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
e.printStackTrace();
System.exit(1);
}catch (NumberFormatException e){
System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
e.printStackTrace();
System.exit(1);
}
semaphore.release();
} catch (InterruptedException e) {
System.out.println("信号量发生错误");
e.printStackTrace();
System.exit(1);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("所有线程格式化日期成功");
}
}
maven中 Failed to read schema document 错误
https://blog.csdn.net/qq_39741730/article/details/104663761
自动 kill 进程再启动,并输出日志文件
# 获取进程名
process_name=new-fleet-market-business-1.0-SNAPSHOT.jar
# 查找进程 ID
pid=$(jps -l | grep $process_name | awk '{print $1}')
# 打印进程 ID
echo "进程 ID 为:$pid"
# 判断进程 ID 是否为空
if [ -n "$pid" ]; then
# 终止进程
kill -9 $pid
echo "停止进程 $pid"
else
echo "没有找到进程 $process_name"
fi
echo "启动程序"
nohup java -Xms512m -Xmx512m -jar ./$process_name >message.log 2>&1 &
数据库保存经纬度采用什么数据类型好?
所以,只需要精确到小数点后7位,精度就是1CM,因此,数据库保存经纬度采用 decimal(10,7)
即可。
问题1:为什么不采用float
?
答:float
,double
容易产生误差,对精确度要求比较高时,建议使用decimal
来存,decimal
在mysql内存是以字符串存储的,
问题2:为什么不用字符串?
答:字符串不方便数据库计算
JAR 包运行用外置配置 yml 文件
启动命令
java -jar -Dspring.config.location=config/application.yml datachangenew.jar
获取当前文件上一条与下一条记录
上一条的sql语句,从news表里按从大到小的顺序选择一条比当前ID小的新闻;
下一条的sql语句,从news表里按从小到大的顺序选择一条比当前ID大的新闻;
如果ID是主键或者有索引,可以直接查找:
方法一:
# 上一条
select * from table_a where id = (select id from table_a where id < {$id} order by id desc limit 1);
# 下一条
select * from table_a where id = (select id from table_a where id > {$id} order by id asc limit 1);
方法二:
# 上一条
select * from table_a where id = (select max(id) from table_a where id < {$id});
# 下一条
select * from table_a where id = (select min(id) from table_a where id > {$id});
synchronized的参数用什么?
很多人用synchronized
(参数)时,随便找个string,hashmap就作为参数了。但是这个参数有什么用呢?synchronized
不就是保证每个进来的线程结束后再放下一个线程进来,对吧?这个例子保证你能明白
class 人
人 你 = new 人();
人 我 = new 人();
如果是
synchronized(你) {
吃饭();
}
那么说明你只能一顿一顿吃,不能同时(多线程)吃好几顿饭;
如果是
synchronized(人.class) {
吃饭();
}
那么只要我在吃饭,你就不能吃饭,得等我吃完!