')
$table. append ( $tr)
$tr. append ( ` ${ user. name} ` )
$tr. append ( ` ${ user. age} ` )
}
} )
} )
} )
script>
Layui
官网:http://layuimini.99php.cn/
文档:http://layuimini.99php.cn/docs/
演示:http://layuimini.99php.cn/onepage/v2/index.html
Github:https://github.com/zhongshaofa/layuimini/tree/v2-onepage
下载:https://codeload.github.com/zhongshaofa/layuimini/zip/refs/heads/v2-onepage
注意 :Layui中的相对路径都是相对于index.html来说的。
注意 :layuimini的表格要求服务器返回的JSON数据格式如下:
{
"code" : 0 ,
"data" : [
{
"id" : 1 ,
"name" : "职业" ,
"value" : "job" ,
"intro" : "一份工作"
} ,
{
"id" : 2 ,
"name" : "性格" ,
"value" : "character" ,
"intro" : "人的性格"
}
] ,
"count" : 87
}
MySQL建议
最大IP地址:255.255 .255 .255
CREATE TABLE user (
age TINYINT UNSIGNED ,
ip VARCHAR ( 15 )
)
CREATE TABLE user (
age TINYINT UNSIGNED ,
ip INT UNSIGNED
)
INSERT INTO user VALUES ( 10 , INET_ATON( '255.255.255.255' ) )
SELECT INET_NTOA( ip) FROM user
注意:
数据字典
使用PowerDesigner:
drop table if exists dict_item;
drop table if exists dict_type;
create table dict_item
(
id bigint not null ,
name varchar ( 20 ) not null ,
value varchar ( 20 ) not null ,
no int not null default 0 comment '用来排序,数字越小,优先级越高,越先展示' ,
type_id bigint comment '该条目所属的数据字典类型' ,
status int not null default 1 comment '是否启用该条目,0:不启用,1:启用' ,
primary key ( id) ,
unique key AK_UK_1 ( name, type_id) ,
unique key AK_UK_2 ( value , type_id)
) ;
alter table dict_item comment '数据字典每一项具体的内容' ;
create table dict_type
(
id bigint not null auto_increment ,
name varchar ( 20 ) not null comment '名称是展示在客户端的,是有可能会发生改变的' ,
value varchar ( 20 ) not null comment '值不会发生改变,编写SQL操作数据时,一般使用value而不是name' ,
intro varchar ( 100 ) comment '防止程序员忘记该数据字典类型的作用、功能(根据项目需求可有可无)' ,
primary key ( id) ,
unique key AK_UK_1 ( name) ,
unique key AK_UK_2 ( value )
) ;
alter table dict_type comment '数据字典类型' ;
alter table dict_item add constraint FK_Reference_1 foreign key ( type_id)
references dict_type ( id) on delete restrict on update restrict ;
使用IDEA自带的数据库工具+不使用外键+数据库使用最优字段类型:
create table jk. dict_type
(
id smallint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 20 ) default '' not null comment '名称' ,
value varchar ( 20 ) default '' not null comment '值' ,
intro varchar ( 100 ) default '' not null comment '简介' ,
constraint dict_type_name_uindex
unique ( name) ,
constraint dict_type_value_uindex
unique ( value )
)
comment '数据字典类型' ;
create table jk. dict_item
(
id smallint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 20 ) default '' not null comment '名称' ,
value varchar ( 20 ) default '' not null comment '值' ,
type_id smallint unsigned not null comment '类型id' ,
sn smallint unsigned default 0 not null comment '排序序号:默认为0,值越大,越优先排列展示' ,
enabled tinyint unsigned default 1 not null comment '是否启用:0,禁用;1,启用;默认为1' ,
constraint dict_item_name_type_id_uindex
unique ( name, type_id) ,
constraint dict_item_value_type_id_uindex
unique ( value , type_id)
)
comment '数据字典条目' ;
客户端:
服务端:
POJO—Query:
@Data
public class PageQuery {
private static final int MIN_SIZE = 1 ;
private static final int DEFAULT_SIZE = 10 ;
private long size;
private long page;
private List < ? > data;
private long total;
private long pages;
public long getSize ( ) {
return size < MIN_SIZE ? DEFAULT_SIZE : size;
}
public long getPage ( ) {
return page < MIN_SIZE ? MIN_SIZE : page;
}
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class KeywordQuery extends PageQuery {
private String keyword;
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class DictTypeQuery extends KeywordQuery {
}
ServiceImpl:
@Service
@Transactional
public class DictTypeServiceImpl extends ServiceImpl < DictTypeMapper , DictType > implements DictTypeService {
@Autowired
private DictTypeMapper mapper;
@Override
@Transactional ( readOnly = true )
public void list ( DictTypeQuery query) {
final String keyword = query. getKeyword ( ) ;
LambdaQueryWrapper < DictType > wrapper = new LambdaQueryWrapper < > ( ) ;
if ( ! StringUtils . isEmpty ( keyword) ) {
wrapper. like ( DictType :: getName , keyword) . or ( )
. like ( DictType :: getIntro , keyword) . or ( )
. like ( DictType :: getValue , keyword) ;
}
wrapper. orderByDesc ( DictType :: getId ) ;
Page < DictType > page = new Page < > ( query. getNo ( ) , query. getSize ( ) ) ;
mapper. selectPage ( page, wrapper) ;
query. setData ( page. getRecords ( ) ) ;
query. setPages ( page. getPages ( ) ) ;
query. setTotal ( page. getTotal ( ) ) ;
query. setSize ( getSize ( ) ) ;
query. setPage ( getCurrent ( ) ) ;
}
}
Controller:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public Map < String , Object > list ( DictTypeQuery query) {
service. list ( query) ;
final Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "msg" , "" ) ;
map. put ( "data" , query. getData ( ) ) ;
map. put ( "count" , query. getTotal ( ) ) ;
map. put ( "code" , 0 ) ;
return map;
}
@PostMapping ( "/remove" )
public Map < String , Object > remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
final Map < String , Object > map = new HashMap < > ( ) ;
if ( service. removeByIds ( Arrays . asList ( ids) ) ) {
map. put ( "msg" , "删除成功" ) ;
map. put ( "code" , 0 ) ;
} else {
map. put ( "msg" , "删除失败" ) ;
map. put ( "code" , 1 ) ;
}
return map;
}
@PostMapping ( "/save" )
public Map < String , Object > save ( DictType dictType) {
final Map < String , Object > map = new HashMap < > ( ) ;
if ( service. saveOrUpdate ( dictType) ) {
map. put ( "msg" , "保存成功" ) ;
map. put ( "code" , 0 ) ;
} else {
map. put ( "msg" , "保存失败" ) ;
map. put ( "code" , 1 ) ;
}
return map;
}
}
封装MyBatis-Plus方便查询
以查询DictType(数据字典类型)为例
enhance—MPPage、MPQueryWrapper:
public class MPPage < T > extends Page < T > {
private final PageQuery query;
public MPPage ( PageQuery query) {
super ( query. getPage ( ) , query. getSize ( ) ) ;
this . query = query;
}
public void updateQuery ( ) {
query. setData ( getRecords ( ) ) ;
query. setPages ( getPages ( ) ) ;
query. setTotal ( getTotal ( ) ) ;
query. setSize ( getSize ( ) ) ;
query. setPage ( getCurrent ( ) ) ;
}
}
public class MPQueryWrapper < T > extends LambdaQueryWrapper < T > {
@SafeVarargs
public final MPQueryWrapper < T > like ( Object val, SFunction < T , ? > . . . funcs) {
if ( val == null || funcs == null || funcs. length == 0 ) return this ;
final String str = val. toString ( ) ;
if ( str. length ( ) == 0 ) return this ;
return ( MPQueryWrapper < T > ) nested ( ( wrapper) -> {
for ( SFunction < T , ? > func : funcs) {
wrapper. like ( func, str) . or ( ) ;
}
} ) ;
}
}
动态代理更新Query对象:
@Configuration
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Aspect
@Component
public class PageMapperInterceptor {
@Around ( "execution(public com.baomidou.mybatisplus.core.metadata.IPage com.baomidou.mybatisplus.core.mapper.BaseMapper.selectPage(com.baomidou.mybatisplus.core.metadata.IPage, com.baomidou.mybatisplus.core.conditions.Wrapper))" )
public Object updateQuery ( ProceedingJoinPoint point) throws Throwable {
Object result = point. proceed ( ) ;
final Object [ ] args = point. getArgs ( ) ;
if ( args != null && args. length > 0 ) {
Object arg = args[ 0 ] ;
if ( arg instanceof MPPage ) {
( ( MPPage < ? > ) arg) . updateQuery ( ) ;
}
}
return result;
}
}
使用—ServiceImpl:
@Transactional
@Service
public class DictTypeServiceImpl extends ServiceImpl < DictTypeMapper , DictType > implements DictTypeService {
@Override
@Transactional ( readOnly = true )
public void list ( DictTypeQuery query) {
MPQueryWrapper < DictType > wrapper = new MPQueryWrapper < > ( ) ;
wrapper. like ( query. getKeyword ( ) , DictType :: getName , DictType :: getValue , DictType :: getIntro ) ;
wrapper. orderByDesc ( DictType :: getId ) ;
baseMapper. selectPage ( new MPPage < > ( query) , wrapper) ;
}
}
Controller:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public Map < String , Object > list ( DictTypeQuery query) {
service. list ( query) ;
final Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "msg" , "" ) ;
map. put ( "data" , query. getData ( ) ) ;
map. put ( "count" , query. getTotal ( ) ) ;
map. put ( "code" , 0 ) ;
return map;
}
}
封装给客户端的返回值
public class R extends HashMap < String , Object > {
public static final int CODE_SUCCESS = 0 ;
private static final String K_CODE = "code" ;
private static final String K_MSG = "msg" ;
private static final String K_DATA = "data" ;
public R setCode ( int code) {
return add ( K_CODE, code) ;
}
public R setMsg ( String msg) {
return add ( K_MSG, msg) ;
}
public R setData ( Object data) {
return add ( K_DATA, data) ;
}
public R add ( String key, Object data) {
put ( key, data) ;
return this ;
}
}
public final class Rs {
private Rs ( ) {
}
public static final int CODE_SUCCESS = 0 ;
public static final int CODE_ERROR_DEFAULT = 1 ;
private static R success ( ) {
return new R ( ) . setCode ( CODE_SUCCESS) ;
}
public static R success ( PageQuery query) {
return success ( ) . setData ( query. getData ( ) ) ;
}
public static R success ( String msg) {
return success ( ) . setMsg ( msg) ;
}
public static R success ( PageQuery query, String msg) {
return success ( ) . setData ( query. getData ( ) ) . setMsg ( msg) ;
}
public static R error ( ) {
return new R ( ) . setCode ( CODE_ERROR_DEFAULT) ;
}
public static R error ( String msg) {
return error ( ) . setMsg ( msg) ;
}
public static R error ( int code, String msg) {
return new R ( ) . setCode ( code) . setMsg ( msg) ;
}
public static R r ( boolean success) {
return new R ( ) . setCode ( success ? CODE_SUCCESS : CODE_ERROR_DEFAULT) ;
}
public static R r ( boolean success, String msg) {
return r ( success) . setMsg ( msg) ;
}
public static R r ( boolean success, Object data) {
return r ( success) . setData ( data) ;
}
}
Controller使用:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( "count" , query. getTotal ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
final boolean success = service. removeByIds ( Arrays . asList ( ids) ) ;
final String msg = success ? "删除成功" : "删除失败" ;
return Rs . r ( success, msg) ;
}
@PostMapping ( "/save" )
public R save ( DictType dictType) {
if ( ! service. saveOrUpdate ( dictType) ) {
throw new RuntimeException ( "保存失败" ) ;
}
return Rs . success ( "保存成功" ) ;
}
}
统一异常处理+HTTP响应状态码
如果服务器端操作失败的话,比如删除失败、保存失败,那么给客户端返回的StatusCode就不应该是200,应该是400/500,原因如下:
客户端(前端)极大可能是根据HTTP请求的响应状态码来判断某个请求是否成功的,而不是通过服务器返回的JSON数据的某个属性值来判断
比如AJAX的回调方法默认就是通过HTTP的响应状态码来判断是否请求成功的。
因此如果服务器处理数据失败,应该修改响应状态码200(OK)为其它StatusCode,比如400、500。
public interface JSONable {
default String jsonString ( ) throws Exception {
return JSONs . getMAPPER ( ) . writeValueAsString ( this ) ;
}
}
public class R extends HashMap < String , Object > implements JSONable {
}
@ControllerAdvice
public class ExceptionInterceptor {
@ExceptionHandler ( Throwable . class )
public void exceptionHandlerOther ( Throwable throwable, HttpServletResponse response) throws Exception {
response. setCharacterEncoding ( "UTF-8" ) ;
response. setStatus ( 400 ) ;
response. getWriter ( ) . write ( Rs . error ( throwable. getMessage ( ) ) . jsonString ( ) ) ;
}
private Throwable getRealCause ( Throwable throwable) {
Throwable cause = throwable. getCause ( ) ;
while ( cause != null ) {
throwable = cause;
cause = cause. getCause ( ) ;
}
return throwable;
}
}
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service. removeByIds ( Arrays . asList ( ids) ) ) {
throw new RuntimeException ( "删除失败" ) ;
}
return Rs . success ( "删除成功" ) ;
}
}
还可以继续封装:
public enum CodeMsg {
BAD_REQUEST ( 400 , "请求出错" ) ,
UNAUTHORIZED ( 401 , "未授权" ) ,
FORBIDDEN ( 403 , "禁止访问" ) ,
NOT_FOUND ( 404 , "资源不存在" ) ,
INTERNAL_SERVER_ERROR ( 500 , "服务器内部错误" ) ,
OPERATE_OK ( R . CODE_SUCCESS, "操作成功" ) ,
SAVE_OK ( R . CODE_SUCCESS, "保存成功" ) ,
REMOVE_OK ( R . CODE_SUCCESS, "删除成功" ) ,
OPERATE_ERROR ( 40001 , "操作失败" ) ,
SAVE_ERROR ( 40002 , "保存失败" ) ,
REMOVE_ERROR ( 40003 , "删除失败" ) ,
UPLOAD_IMG_ERROR ( 40004 , "图片上传失败" ) ,
WRONG_USERNAME ( 50001 , "用户名不存在" ) ,
WRONG_PASSWORD ( 50002 , "密码错误" ) ,
USER_LOCKED ( 50003 , "用户被锁定,无法正常登录" ) ,
WRONG_CAPTCHA ( 50004 , "验证码错误" ) ,
NO_TOKEN ( 60001 , "没有Token,请登录" ) ,
TOKEN_EXPIRED ( 60002 , "Token过期,请重新登录" ) ,
NO_PERMISSION ( 60003 , "没有相关的操作权限" ) ;
private final int code;
private final String msg;
CodeMsg ( int code, String msg) {
this . code = code;
this . msg = msg;
}
public int getCode ( ) {
return code;
}
public String getMsg ( ) {
return msg;
}
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class CommonException extends RuntimeException {
private int code;
public CommonException ( ) {
this ( CodeMsg . BAD_REQUEST. getCode ( ) , null ) ;
}
public CommonException ( String msg) {
this ( msg, null ) ;
}
public CommonException ( int code, String msg) {
this ( code, msg, null ) ;
}
public CommonException ( String msg, Throwable cause) {
this ( CodeMsg . BAD_REQUEST. getCode ( ) , msg, cause) ;
}
public CommonException ( int code, String msg, Throwable cause) {
super ( msg, cause) ;
this . code = code;
}
public CommonException ( CodeMsg codeMsg) {
this ( codeMsg, null ) ;
}
public CommonException ( CodeMsg codeMsg, Throwable cause) {
this ( codeMsg. getCode ( ) , codeMsg. getMsg ( ) , cause) ;
}
public int getCode ( ) {
return code;
}
}
public final class Rs {
private Rs ( ) {
}
public static final String K_COUNT = "count" ;
private static final int CODE_SUCCESS = 0 ;
private static final int CODE_ERROR_DEFAULT = CodeMsg . BAD_REQUEST. getCode ( ) ;
private static R success ( ) {
return new R ( ) . setCode ( CODE_SUCCESS) ;
}
public static R success ( PageQuery query) {
return success ( ) . setData ( query. getData ( ) ) ;
}
public static R success ( String msg) {
return success ( ) . setMsg ( msg) ;
}
public static R success ( Object data) {
return success ( ) . setData ( data) ;
}
public static R success ( PageQuery query, String msg) {
return success ( ) . setData ( query. getData ( ) ) . setMsg ( msg) ;
}
public static R success ( CodeMsg codeMsg) {
return success ( ) . setMsg ( codeMsg. getMsg ( ) ) ;
}
public static R error ( ) {
return error ( CODE_ERROR_DEFAULT) ;
}
public static R error ( int code) {
return new R ( ) . setCode ( code) ;
}
public static R error ( String msg) {
return error ( ) . setMsg ( msg) ;
}
public static R error ( int code, String msg) {
return error ( code) . setMsg ( msg) ;
}
public static R error ( Throwable e) {
R r = error ( ) ;
if ( e instanceof CommonException ) {
r. setCode ( ( ( CommonException ) e) . getCode ( ) ) ;
}
return r;
}
public static R r ( boolean success) {
return new R ( ) . setCode ( success ? CODE_SUCCESS : CODE_ERROR_DEFAULT) ;
}
public static R r ( boolean success, String msg) {
return r ( success) . setMsg ( msg) ;
}
public static R r ( boolean success, Object data) {
return r ( success) . setData ( data) ;
}
public static R exception ( String msg) {
throw new CommonException ( msg) ;
}
public static R exception ( CodeMsg codeMsg) {
throw new CommonException ( codeMsg) ;
}
}
@ControllerAdvice
public class ExceptionInterceptor {
@ExceptionHandler ( Throwable . class )
public void handle ( Throwable throwable,
HttpServletResponse response) throws Exception {
response. setContentType ( MediaType . APPLICATION_JSON_VALUE) ;
response. setCharacterEncoding ( StandardCharsets . UTF_8. displayName ( ) ) ;
response. setStatus ( 400 ) ;
response. getWriter ( ) . write ( Rs . error ( throwable) . json ( ) ) ;
}
private Throwable getRealCause ( Throwable throwable) {
Throwable cause = throwable. getCause ( ) ;
while ( cause != null ) {
throwable = cause;
cause = cause. getCause ( ) ;
}
return throwable;
}
}
其实异常处理器还有更简便的写法:
@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler {
@ExceptionHandler ( Throwable . class )
@ResponseStatus ( code = HttpStatus . BAD_REQUEST)
public JSONResult handle ( Throwable throwable) {
log. error ( "error" , throwable) ;
return JSONResults . exception ( throwable) ;
}
}
Controller使用:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( Rs . K_COUNT, query. getTotal ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service. removeByIds ( Arrays . asList ( ids) ) ) {
throw new CommonException ( CodeMsg . REMOVE_ERROR) ;
}
return Rs . success ( CodeMsg . REMOVE_OK) ;
}
@PostMapping ( "/save" )
public R save ( DictType dictType) {
if ( ! service. saveOrUpdate ( dictType) ) {
throw new CommonException ( CodeMsg . SAVE_ERROR) ;
}
return Rs . success ( CodeMsg . SAVE_OK. getMsg ( ) ) ;
}
}
还可以将Controller中的公共代码抽取出来:
public abstract class BaseController < T > {
protected abstract IService < T > service ( ) ;
@GetMapping ( "/list" )
public R list ( ) {
return Rs . success ( service ( ) . list ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service ( ) . removeByIds ( Arrays . asList ( ids) ) ) {
Rs . exception ( CodeMsg . REMOVE_ERROR) ;
}
return Rs . success ( CodeMsg . REMOVE_OK) ;
}
@PostMapping ( "/save" )
public R save ( T entity) {
if ( ! service ( ) . saveOrUpdate ( entity) ) {
Rs . exception ( CodeMsg . SAVE_ERROR) ;
}
return Rs . success ( CodeMsg . SAVE_OK) ;
}
}
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController extends BaseController < DictType > {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( Rs . K_COUNT, query. getTotal ( ) ) ;
}
@Override
protected IService < DictType > service ( ) {
return service;
}
}
统一异常处理—配合Shiro
@RestControllerAdvice
只能拦截到Controller抛出的异常
public class ErrorFilter implements Filter {
public static final String ERROR_URI = "/handleError" ;
@Override
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException , ServletException {
try {
chain. doFilter ( request, response) ;
} catch ( Exception e) {
request. setAttribute ( ERROR_URI, e) ;
request. getRequestDispatcher ( ERROR_URI) . forward ( request, response) ;
}
}
}
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean < Filter > filterRegistrationBean ( ) {
FilterRegistrationBean < Filter > bean = new FilterRegistrationBean < > ( ) ;
bean. setFilter ( new ErrorFilter ( ) ) ;
bean. addUrlPatterns ( "/*" ) ;
bean. setOrder ( Ordered . HIGHEST_PRECEDENCE) ;
return bean;
}
}
@RestController
public class ErrorController {
@RequestMapping ( ErrorFilter . ERROR_URI)
public void handle ( HttpServletRequest request) throws Exception {
throw ( Exception ) request. getAttribute ( ErrorFilter . ERROR_URI) ;
}
}
@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler {
@ExceptionHandler ( Throwable . class )
@ResponseStatus ( code = HttpStatus . BAD_REQUEST)
public JSONResult handle ( Throwable t) {
log. error ( "handle" , t) ;
if ( t instanceof CommonException ) {
return handle ( ( CommonException ) t) ;
} else if ( t instanceof BindException ) {
return handle ( ( BindException ) t) ;
} else if ( t instanceof ConstraintViolationException ) {
return handle ( ( ConstraintViolationException ) t) ;
} else if ( t instanceof AuthorizationException ) {
return JSONResults . error ( CodeMsg . NO_PERMISSION) ;
}
Throwable cause = t. getCause ( ) ;
if ( cause != null ) {
return handle ( cause) ;
}
return JSONResults . error ( ) ;
}
private JSONResult handle ( CommonException ce) {
return JSONResults . error ( ce. getCode ( ) , ce. getMessage ( ) ) ;
}
private JSONResult handle ( BindException be) {
List < ObjectError > errors = be. getBindingResult ( ) . getAllErrors ( ) ;
List < String > defaultMsgs = Streams . map ( errors, ObjectError :: getDefaultMessage ) ;
String msg = StringUtils . collectionToDelimitedString ( defaultMsgs, ", " ) ;
return JSONResults . error ( msg) ;
}
private JSONResult handle ( ConstraintViolationException cve) {
List < String > msgs = Streams . map ( cve. getConstraintViolations ( ) , ConstraintViolation :: getMessage ) ;
String msg = StringUtils . collectionToDelimitedString ( msgs, ", " ) ;
return JSONResults . error ( msg) ;
}
}
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean ( Realm realm) {
ShiroFilterFactoryBean filterBean = new ShiroFilterFactoryBean ( ) ;
filterBean. setSecurityManager ( new DefaultWebSecurityManager ( realm) ) ;
Map < String , Filter > filters = new HashMap < > ( ) ;
filters. put ( "token" , new TokenFilter ( ) ) ;
filterBean. setFilters ( filters) ;
Map < String , String > urlMap = new LinkedHashMap < > ( ) ;
urlMap. put ( ErrorFilter . ERROR_URI, "anon" ) ;
urlMap. put ( "/**" , "token" ) ;
filterBean. setFilterChainDefinitionMap ( urlMap) ;
return filterBean;
}
}
数据的一致性
外键有优点,同样,也有它的缺点
如果在项目中使用了外键,那么表与表之间的数据一致性其实是不用我们操心的,因为有外键自动帮我们约束。
因此对于那些小型的、对于数据一致性要求很高的项目,需要使用外键。
但是大型的、分布式的互联网项目出于对数据库的性能、备份、迁移、维护等原因的考虑,一般而言在设计数据库时是不使用外键的,那么这时表与表之间的联系,也就是数据一致性问题怎么解决呢?
答案就是需要我们自己在应用层做好相应的处理,但做好这个处理并不简单。
假设现在要对某张表的进行数据一致性的处理,有许多非常麻烦的点
虽然数据库没有使用外键,但对业务来讲,表与表之间应该是有联系的
那么对这张表进行删除、更新、添加等操作都需要考虑数据一致性,具体到代码,就是remove、save、update这种方法有很多,需要都考虑到
有可能会有很多表在数据上都关联这张表,因此我们需要清楚每一张表与每一张表之间的关联关系,知道了表与表之间的关联关系后,才能去逐个处理
解决方案:自己写一个保证数据一致性的小框架,这个小框架的特点:
MJ老师编写框架经验:
如果你自己想写一个比较好用的框架
首先应该从应用的角度出发,先从使用者(自己、其他开发者)的角度出发
考虑别人应该怎么用这个框架、这个框架能够怎样简化开发、这个框架怎么样能够使开发变得更爽、更高效、更敏捷
然后再考虑减少BUG
然后再考虑安全问题
然后再考虑性能问题
然后再考虑解耦、抽取、可扩展…
拼音库—tinypinyin的使用
< dependency>
< groupId> com.github.promeg groupId>
< artifactId> tinypinyin artifactId>
< version> 2.0.3 version>
dependency>
@Override
public boolean updateById ( PlateRegion entity) {
processPinyin ( entity) ;
return super . updateById ( entity) ;
}
@Override
public boolean save ( PlateRegion entity) {
processPinyin ( entity) ;
return super . save ( entity) ;
}
private void processPinyin ( PlateRegion entity) {
final String name = entity. getName ( ) ;
if ( StringUtils . isEmpty ( name) ) return ;
entity. setPinyin ( Pinyin . toPinyin ( name, "_" ) ) ;
}
MapStruct
作用:对象转换
< dependency>
< groupId> org.mapstruct groupId>
< artifactId> mapstruct artifactId>
< version> ${map.struct.version} version>
< scope> provided scope>
dependency>
< dependency>
< groupId> org.mapstruct groupId>
< artifactId> mapstruct-processor artifactId>
< version> ${map.struct.version} version>
< scope> provided scope>
dependency>
@Mapper
public interface MapStruct {
MapStruct INSTANCE = Mappers . getMapper ( MapStruct . class ) ;
DictItem vo2po ( ReqSaveDictItem reqSaveVo) ;
DictType vo2po ( ReqSaveDictType reqSaveVo) ;
ExamPlace vo2po ( ReqSaveExamPlace reqSaveVo) ;
ExamPlaceCourse vo2po ( ReqSaveExamPlaceCourse reqSaveVo) ;
PlateRegion vo2po ( ReqSavePlateRegion reqSaveVo) ;
RespDictItem po2vo ( DictItem po) ;
RespDictType po2vo ( DictType po) ;
RespExamPlace po2vo ( ExamPlace po) ;
RespExamPlaceCourse po2vo ( ExamPlaceCourse po) ;
RespPlateRegion po2vo ( PlateRegion po) ;
}
基本使用:
final DictItem dictItem = MapStruct . INSTANCE. vo2po ( new ReqSaveDictItem ( ) ) ;
final RespDictItem respDictItem = MapStruct . INSTANCE. po2vo ( new DictItem ( ) ) ;
项目中使用:
public abstract class BaseController < T , ReqSave > {
protected abstract Function < ReqSave , T > function ( ) ;
@PostMapping ( "/save" )
public JSONResult save ( @Valid ReqSave entity) {
service. saveOrUpdate ( function ( ) . apply ( entity) ) ;
}
}
public class DictItemController extends BaseController < DictItem , ReqSaveDictItem > {
@Override
protected Function < ReqSaveDictItem , DictItem > function ( ) {
return MapStruct . INSTANCE:: vo2po ;
}
}
public class PlateRegionServiceImpl extends ServiceImpl < PlateRegionMapper , PlateRegion > implements PlateRegionService {
public JSONDataResult < List < RespPlateRegion > > listProvinces ( ) {
final List < RespPlateRegion > data = baseMapper. selectList ( wrapper)
. stream ( ) . map ( MapStruct . INSTANCE:: po2vo )
. collect ( Collectors . toList ( ) ) ;
return JSONResults . success ( data) ;
}
}
自定义转换规则 :
public class MapStructFormatter {
@Qualifier
@Target ( ElementType . METHOD)
@Retention ( RetentionPolicy . CLASS)
public @interface Date2Millis { }
@Date2Millis
public static Long date2millis ( Date date) {
if ( date == null ) return null ;
return date. getTime ( ) ;
}
}
@Data
public class SysUser {
private Date loginTime;
}
@Data
@ApiModel ( "系统用户" )
public class RespSysUser {
@ApiModelProperty ( "最后一次登录的时间" )
private Long loginTime;
}
@Mapper ( uses = {
MapStructFormatter . class
} )
public interface MapStructs {
@Mapping ( source = "loginTime" ,
target = "loginTime" ,
qualifiedBy = MapStructFormatter. Date2Millis . class )
RespSysUser po2vo ( SysUser po) ;
}
登录—简单登录
< dependency>
< groupId> com.github.whvcse groupId>
< artifactId> easy-captcha artifactId>
< version> 1.6.2 version>
dependency>
@RestController
@RequestMapping ( "/sysUsers" )
@Api ( tags = "系统用户" , description = "SysUser" )
public class SysUserController extends BaseController < SysUser , ReqSaveSysUser > {
@Autowired
private SysUserService service;
@GetMapping ( "/captcha" )
@ApiOperation ( "生成验证码" )
public void captcha ( HttpServletRequest request,
HttpServletResponse response) throws Exception {
CaptchaUtil . out ( request, response) ;
}
@PostMapping ( "/login" )
@ApiOperation ( "登录" )
public JSONDataResult < RespLogin > login ( ReqLogin reqVo, HttpServletRequest request) {
if ( CaptchaUtil . ver ( reqVo. getCaptcha ( ) , request) ) {
return JSONResults . success ( service. login ( reqVo) ) ;
}
JSONResults . exception ( CodeMsg . WRONG_CAPTCHA) ;
return null ;
}
}
@Override
public RespLogin login ( ReqLogin reqVo) {
MPLambdaQueryWrapper < SysUser > wrapper = new MPLambdaQueryWrapper < > ( ) ;
wrapper. eq ( SysUser :: getUsername , reqVo. getUsername ( ) ) ;
SysUser po = baseMapper. selectOne ( wrapper) ;
if ( po == null ) {
return JsonVos . raise ( CodeMsg . WRONG_USERNAME) ;
}
if ( ! po. getPassword ( ) . equals ( reqVo. getPassword ( ) ) ) {
return JsonVos . raise ( CodeMsg . WRONG_PASSWORD) ;
}
if ( po. getStatus ( ) == Constants. SysUserStatus . LOCKED) {
return JsonVos . raise ( CodeMsg . USER_LOCKED) ;
}
po. setLoginTime ( new Date ( ) ) ;
baseMapper. updateById ( po) ;
RespLogin vo = MapStruct . INSTANCE. po2loginVo ( po) ;
return vo;
}
前端Ajax登录:
Ajaxs. loadPost ( {
uri : 'sysUsers/login' ,
data : data. field,
success : ( response ) => {
location. href = '../index.html'
} ,
xhrFields : {
withCredentials : true
}
}
登录—Token
后端
登录
@Data
@ApiModel ( "登录成功的结果" )
public class RespLogin {
@ApiModelProperty ( "登录令牌" )
private String token;
}
@PostMapping ( "/login" )
@ApiOperation ( "登录" )
public JSONDataResult < RespLogin > login ( ReqLogin reqVo, HttpServletRequest request) {
if ( CaptchaUtil . ver ( reqVo. getCaptcha ( ) , request) ) {
return JSONResults . success ( service. login ( reqVo) ) ;
}
JSONResults . exception ( CodeMsg . WRONG_CAPTCHA) ;
return null ;
}
@Override
public RespLogin login ( ReqLogin reqVo) {
MPLambdaQueryWrapper < SysUser > wrapper = new MPLambdaQueryWrapper < > ( ) ;
wrapper. eq ( SysUser :: getUsername , reqVo. getUsername ( ) ) ;
SysUser po = baseMapper. selectOne ( wrapper) ;
if ( po == null ) {
return JsonVos . raise ( CodeMsg . WRONG_USERNAME) ;
}
if ( ! po. getPassword ( ) . equals ( reqVo. getPassword ( ) ) ) {
return JsonVos . raise ( CodeMsg . WRONG_PASSWORD) ;
}
if ( po. getStatus ( ) == Constants. SysUserStatus . LOCKED) {
return JsonVos . raise ( CodeMsg . USER_LOCKED) ;
}
po. setLoginTime ( new Date ( ) ) ;
baseMapper. updateById ( po) ;
String token = UUID. randomUUID ( ) . toString ( ) ;
Caches . putToken ( token, po) ;
RespLogin vo = MapStruct . INSTANCE. po2loginVo ( po) ;
vo. setToken ( token) ;
return vo;
}
退出登录
@PostMapping ( "/logout" )
@ApiOperation ( "退出登录" )
public JSONResult logout ( @RequestHeader ( "Token" ) String token) {
Caches . removeToken ( token) ;
return JSONResults . success ( CodeMsg . LOGOUT_OK) ;
}
前端
登录:
class DataKey {
static USER = 'user'
static TOKEN = 'token'
static TOKEN_HEADER = 'Token'
}
Ajaxs. loadPost ( {
uri : 'sysUsers/login' ,
data : data. field,
success : ( response ) => {
Datas. save ( DataKey. USER , response. data)
location. href = '../index.html'
} ,
xhrFields : {
withCredentials : true
}
} )
需要确保登录后的每次请求都带上Token信息
static get ( ) {
let ret = layui. data ( this . TABLE )
for ( let i = 0 ; i < arguments. length; i++ ) {
if ( ! ret) return null
ret = ret[ arguments[ i] ]
}
return ret
}
static _addTokenHeader ( cfg ) {
const token = Datas. get ( DataKey. USER , DataKey. TOKEN )
if ( token) {
if ( ! cfg. headers) {
cfg. headers = { }
}
cfg. headers[ DataKey. TOKEN_HEADER ] = token
}
}
static ajax ( cfg ) {
cfg. url = Commons. url ( cfg. uri)
Commons. _addTokenHeader ( cfg)
$. ajax ( cfg)
}
_init ( ) {
const cfg = this . _commonCfg ( )
cfg. url = Commons. url ( this . _cfg. uri)
$. extend ( cfg, this . _cfg)
cfg. elem = cfg. selector
Commons. _addTokenHeader ( cfg)
this . _innerTable = this . _layuiTable ( ) . render ( cfg)
this . _cfg = cfg
}
登出:
$ ( '.login-out' ) . click ( ( ) => {
Ajaxs. loadPost ( {
uri : 'sysUsers/logout' ,
success : ( ) => {
Datas. remove ( DataKey. USER )
Layers. msgSuccess ( '退出登录成功' , ( ) => {
location. href = 'page/login.html'
} )
}
} )
} )
权限管理—RBAC
可以登录后台管理系统的员工/系统管理员:比如:sys_user
(表名以sys_
开头)
使用产品的用户/客户(APP、小程序、网页):比如:user
表结构设计 :
可以登录后台管理系统的员工/系统管理员:比如:sys_user(表名以sys_开头)
使用产品的用户/客户(APP、小程序、网页):比如:user
create table if not exists jk. sys_resource
(
id tinyint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 15 ) default '' not null comment '名称' ,
uri varchar ( 100 ) default '' not null comment '链接地址' ,
permission varchar ( 100 ) default '' not null comment '权限标识' ,
type tinyint unsigned default 0 not null comment '资源类型(0是目录,1是菜单,2是按钮)PS:按钮就是增删改查之类的能点击的' ,
icon varchar ( 100 ) default '' not null comment '图标' ,
sn tinyint unsigned default 0 not null comment '序号' ,
parent_id tinyint unsigned default 0 not null comment '父资源id' ,
constraint sys_resource_parent_id_name_uindex
unique ( parent_id, name)
)
comment '资源' ;
create table if not exists jk. sys_role
(
id tinyint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 15 ) default '' not null comment '角色名称' ,
constraint sys_role_name_uindex
unique ( name)
)
comment '角色' ;
create table if not exists jk. sys_role_resource
(
role_id tinyint unsigned default 0 not null comment '角色id' ,
resource_id tinyint unsigned default 0 not null comment '资源id' ,
primary key ( resource_id, role_id)
)
comment '角色-资源' ;
create table if not exists jk. sys_user
(
id smallint unsigned auto_increment comment '主键'
primary key ,
nickname varchar ( 15 ) default '' not null comment '昵称' ,
username varchar ( 15 ) default '' not null comment '登录用的用户名' ,
password char ( 32 ) default '' not null comment '登录用的密码,密码经过MD5加密之后就是32位的字符串' ,
create_time datetime default CURRENT_TIMESTAMP not null comment '创建的时间' ,
login_time datetime null comment '最后一次登录的时间' ,
status tinyint unsigned default 0 not null comment '账号的状态,0是正常,1是锁定' ,
constraint sys_user_username_uindex
unique ( username)
)
comment '用户(可以登录后台系统的)' ;
create table if not exists jk. sys_user_role
(
role_id tinyint unsigned default 0 not null comment '角色id' ,
user_id smallint unsigned default 0 not null comment '用户id' ,
primary key ( user_id, role_id)
)
comment '用户-角色' ;
逻辑删除
逻辑删除—MyBatisPlus
create table user
(
id int unsigned auto_increment
primary key ,
name varchar ( 15 ) default '' not null ,
deleted tinyint unsigned default 0 not null comment '1是被删除,0是未删除' ,
constraint user_name_uindex
unique ( name)
) ;
企业级文件上传
@RequestBody修饰的请求参数
前端/客户端将Content-Type改为:application/json
请求体传递符合要求的JSON字符串【JSON优势:灵活、前端处理方便、第三方库也多】
@PostMapping ( "/save1" )
@ApiOperation ( "添加或更新" )
public JsonVo save1 ( User user) {
JsonVo jsonVo = new JsonVo ( ) ;
jsonVo. setMsg ( service. saveOrUpdate ( user) ? "保存成功" : "保存失败" ) ;
return jsonVo;
}
@PostMapping ( "/save2" )
@ApiOperation ( "添加或更新" )
public JsonVo save2 ( @RequestBody User user) {
JsonVo jsonVo = new JsonVo ( ) ;
jsonVo. setMsg ( service. saveOrUpdate ( user) ? "保存成功" : "保存失败" ) ;
return jsonVo;
}
单元测试
Spring单元测试
SpringBoot单元测试
< dependency>
< groupId> org.springframework.boot groupId>
< artifactId> spring-boot-starter-test artifactId>
< scope> test scope>
dependency>
@RunWith ( SpringRunner . class )
@SpringBootTest
public class SpringAMQPTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendSimpleQueue ( ) {
String queueName = "simple.queue" ;
Object message = "Hello, SpringAMQP! I am LP!" ;
rabbitTemplate. convertAndSend ( queueName, message) ;
}
}
打包部署
打包部署—jar
打包部署—war
注意
配置JackSON将Model转为JSON时,不包含值为null的属性:
application.yml:
spring :
jackson :
default-property-inclusion : non_null
Java代码:
public final class JSONs {
private JSONs ( ) {
}
private static final ObjectMapper MAPPER = new ObjectMapper ( ) ;
static {
MAPPER. setDefaultPropertyInclusion ( JsonInclude. Include . NON_NULL) ;
}
public static ObjectMapper getMAPPER ( ) {
return MAPPER;
}
}
MySQL配置:
#url=jdbc:mysql://127.0.0.1:3306/lp_resume
#url=jdbc:mysql://localhost:3306/lp_resume
#url=jdbc:mysql:///lp_resume
#UTC:世界同一时间
#url=jdbc:mysql:///lp_resume?serverTimezone=UTC&useSSL=false
#中国时间:serverTimezone=Asia/Shanghai == serverTimezone=GMT+8
#url=jdbc:mysql:///lp_resume?serverTimezone=GMT+8&useSSL=false
url=jdbc:mysql:///lp_resume?serverTimezone=Asia/Shanghai&useSSL=false
############使用IDEA连接数据库############
#使用IDEA连接MySQL数据库时,由于“+”是一个特殊字符,因此需要编码处理为:“%2B”
#例如:jdbc:mysql:///?serverTimezone=GMT%2B8&useSSL=false
#或者:jdbc:mysql:///?serverTimezone=Asia/Shanghai&useSSL=false
HTML的button标签,默认类型是
,因此button如果是其它类型的话,最好显示声明button的type,比如:
客户端向服务器发送请求参数时
如果http://localhost:8080/jk/dictTypes/list?page=1&size=20
,那么服务器获取到的keyword
就是null
如果http://localhost:8080/jk/dictTypes/list?page=1&size=20&keyword=
,那么服务器获取到的keyword
就是""(空字符串)
数据库中,表名和字段名建议使用``、字符串建议使用’'(单引号)
MySQL数据库,行(记录)从0开始,列(字段)从1开始
标准JSON格式:key使用""(双引号):
[
{
"age" : 10 ,
"name" : "lp"
} ,
{
"age" : 20 ,
"name" : "ruoyu"
}
]
{
"string" : "value" ,
"integer" : 10 ,
"bool" : true ,
"null" : null ,
"array" : [ ] ,
"obj" : { }
}
补充:ChromeJSON插件
https://chrome.google.com/webstore/detail/json-handle/iahnhfdhidomcpggpaimmmahffihkfnj
参考
小码哥-李明杰: Java从0到架构师③进阶互联网架构师.
本文完,感谢您的关注支持!
你可能感兴趣的:(JAVA,java,spring,boot,MyBatisPlus,Shiro)
Long类型前后端数据不一致
igotyback
前端
响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问
LocalDateTime 转 String
igotyback
java 开发语言
importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassMain{publicstaticvoidmain(String[]args){//获取当前时间LocalDateTimenow=LocalDateTime.now();//定义日期格式化器DateTimeFormatterformat
Linux下QT开发的动态库界面弹出操作(SDL2)
13jjyao
QT类 qt 开发语言 sdl2 linux
需求:操作系统为linux,开发框架为qt,做成需带界面的qt动态库,调用方为java等非qt程序难点:调用方为java等非qt程序,也就是说调用方肯定不带QApplication::exec(),缺少了这个,QTimer等事件和QT创建的窗口将不能弹出(包括opencv也是不能弹出);这与qt调用本身qt库是有本质的区别的思路:1.调用方缺QApplication::exec(),那么我们在接口
DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理
STU学生网页设计
网页设计 期末网页作业 html静态网页 html5期末大作业 网页设计 web大作业
️精彩专栏推荐作者主页:【进入主页—获取更多源码】web前端期末大作业:【HTML5网页期末作业(1000套)】程序员有趣的告白方式:【HTML七夕情人节表白网页制作(110套)】文章目录二、网站介绍三、网站效果▶️1.视频演示2.图片演示四、网站代码HTML结构代码CSS样式代码五、更多源码二、网站介绍网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程
【华为OD机试真题2023B卷 JAVA&JS】We Are A Team
若博豆
java 算法 华为 javascript
华为OD2023(B卷)机试题库全覆盖,刷题指南点这里WeAreATeam时间限制:1秒|内存限制:32768K|语言限制:不限题目描述:总共有n个人在机房,每个人有一个标号(1<=标号<=n),他们分成了多个团队,需要你根据收到的m条消息判定指定的两个人是否在一个团队中,具体的:1、消息构成为:abc,整数a、b分别代
关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript
二挡起步
web前端期末大作业 javascript html css 旅游 风景
⛵源码获取文末联系✈Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业|游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作|HTML期末大学生网页设计作业,Web大学生网页HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScrip
HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动
二挡起步
web前端期末大作业 web设计网页规划与设计 html css javascript dreamweaver 前端
Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线
node.js学习
小猿L
node.js node.js 学习 vim
node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行
SpringBlade dict-biz/list 接口 SQL 注入漏洞
文章永久免费只为良心
oracle 数据库
SpringBladedict-biz/list接口SQL注入漏洞POC:构造请求包查看返回包你的网址/api/blade-system/dict-biz/list?updatexml(1,concat(0x7e,md5(1),0x7e),1)=1漏洞概述在SpringBlade框架中,如果dict-biz/list接口的后台处理逻辑没有正确地对用户输入进行过滤或参数化查询(PreparedSta
Java 重写(Override)与重载(Overload)
叨唧唧的
Java重写(Override)与重载(Overload)重写(Override)重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如:父类的一个方法申明了一个检查异常IOExceptio
简单了解 JVM
记得开心一点啊
jvm
目录♫什么是JVM♫JVM的运行流程♫JVM运行时数据区♪虚拟机栈♪本地方法栈♪堆♪程序计数器♪方法区/元数据区♫类加载的过程♫双亲委派模型♫垃圾回收机制♫什么是JVMJVM是JavaVirtualMachine的简称,意为Java虚拟机。虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统(如:JVM、VMwave、VirtualBox)。JVM和其他两个虚拟机
1分钟解决 -bash: mvn: command not found,在Centos 7中安装Maven
Energet!c
开发语言
1分钟解决-bash:mvn:commandnotfound,在Centos7中安装Maven检查Java环境1下载Maven2解压Maven3配置环境变量4验证安装5常见问题与注意事项6总结检查Java环境Maven依赖Java环境,请确保系统已经安装了Java并配置了环境变量。可以通过以下命令检查:java-version如果未安装,请先安装Java。1下载Maven从官网下载:前往Apach
Java企业面试题3
马龙强_
java
1.break和continue的作用(智*图)break:用于完全退出一个循环(如for,while)或一个switch语句。当在循环体内遇到break语句时,程序会立即跳出当前循环体,继续执行循环之后的代码。continue:用于跳过当前循环体中剩余的部分,并开始下一次循环。如果是在for循环中使用continue,则会直接进行条件判断以决定是否执行下一轮循环。2.if分支语句和switch分
JVM、JRE和 JDK:理解Java开发的三大核心组件
Y雨何时停T
Java java
Java是一门跨平台的编程语言,它的成功离不开背后强大的运行环境与开发工具的支持。在Java的生态中,JVM(Java虚拟机)、JRE(Java运行时环境)和JDK(Java开发工具包)是三个至关重要的核心组件。本文将探讨JVM、JDK和JRE的区别,帮助你更好地理解Java的运行机制。1.JVM:Java虚拟机(JavaVirtualMachine)什么是JVM?JVM,即Java虚拟机,是Ja
Java面试题精选:消息队列(二)
芒果不是芒
Java面试题精选 java kafka
一、Kafka的特性1.消息持久化:消息存储在磁盘,所以消息不会丢失2.高吞吐量:可以轻松实现单机百万级别的并发3.扩展性:扩展性强,还是动态扩展4.多客户端支持:支持多种语言(Java、C、C++、GO、)5.KafkaStreams(一个天生的流处理):在双十一或者销售大屏就会用到这种流处理。使用KafkaStreams可以快速的把销售额统计出来6.安全机制:Kafka进行生产或者消费的时候会
白骑士的Java教学基础篇 2.5 控制流语句
白骑士所长
Java 教学 java 开发语言
欢迎继续学习Java编程的基础篇!在前面的章节中,我们了解了Java的变量、数据类型和运算符。接下来,我们将探讨Java中的控制流语句。控制流语句用于控制程序的执行顺序,使我们能够根据特定条件执行不同的代码块,或重复执行某段代码。这是编写复杂程序的基础。通过学习这一节内容,你将掌握如何使用条件语句和循环语句来编写更加灵活和高效的代码。条件语句条件语句用于根据条件的真假来执行不同的代码块。if语句‘
python语法——三目运算符
HappyRocking
python python 三目运算符
在java中,有三目运算符,如:intc=(a>b)?a:b表示c取两者中的较大值。但是在python,不能直接这样使用,估计是因为冒号在python有分行的关键作用。那么在python中,如何实现类似功能呢?可以使用ifelse语句,也是一行可以完成,格式为:aifbelsec表示如果b为True,则表达式等于a,否则等于c。如:c=(aif(a>b)elseb)同样是完成了取最大值的功能。
spring如何整合druid连接池?
惜.己
spring spring junit 数据库 java idea 后端 xml
目录spring整合druid连接池1.新建maven项目2.新建mavenModule3.导入相关依赖4.配置log4j2.xml5.配置druid.xml1)xml中如何引入properties2)下面是配置文件6.准备jdbc.propertiesJDBC配置项解释7.配置druid8.测试spring整合druid连接池1.新建maven项目打开IDE(比如IntelliJIDEA,Ecl
ArrayList 源码解析
程序猿进阶
Java基础 ArrayList List java 面试 性能优化 架构设计 idea
ArrayList是Java集合框架中的一个动态数组实现,提供了可变大小的数组功能。它继承自AbstractList并实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量capacity,表示底层数组的实际大小,容器内存储元素的个数不能多于当前容量。当向容器中添
Java爬虫框架(一)--架构设计
狼图腾-狼之传说
java 框架 java 任务 html解析器 存储 电子商务
一、架构图那里搜网络爬虫框架主要针对电子商务网站进行数据爬取,分析,存储,索引。爬虫:爬虫负责爬取,解析,处理电子商务网站的网页的内容数据库:存储商品信息索引:商品的全文搜索索引Task队列:需要爬取的网页列表Visited表:已经爬取过的网页列表爬虫监控平台:web平台可以启动,停止爬虫,管理爬虫,task队列,visited表。二、爬虫1.流程1)Scheduler启动爬虫器,TaskMast
Java:爬虫框架
dingcho
Java java 爬虫
一、ApacheNutch2【参考地址】Nutch是一个开源Java实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。Nutch致力于让每个人能很容易,同时花费很少就可以配置世界一流的Web搜索引擎.为了完成这一宏伟的目标,Nutch必须能够做到:每个月取几十亿网页为这些网页维护一个索引对索引文件进行每秒上千次的搜索提供高质量的搜索结果简单来说Nutch支持分
python怎么将png转为tif_png转tif
weixin_39977276
发国外的文章要求图片是tif,cmyk色彩空间的。大小尺寸还有要求。比如网上大神多,找到了一段代码,感谢!https://www.jianshu.com/p/ec2af4311f56https://github.com/KevinZc007/image2Tifimportjava.awt.image.BufferedImage;importjava.io.File;importjava.io.Fi
SpringCloudAlibaba—Sentinel(限流)
菜鸟爪哇
前言:自己在学习过程的记录,借鉴别人文章,记录自己实现的步骤。借鉴文章:https://blog.csdn.net/u014494148/article/details/105484410Sentinel介绍Sentinel诞生于阿里巴巴,其主要目标是流量控制和服务熔断。Sentinel是通过限制并发线程的数量(即信号隔离)来减少不稳定资源的影响,而不是使用线程池,省去了线程切换的性能开销。当资源
springboot+vue项目实战一-创建SpringBoot简单项目
苹果酱0567
面试题汇总与解析 spring boot 后端 java 中间件 开发语言
这段时间抽空给女朋友搭建一个个人博客,想着记录一下建站的过程,就当做笔记吧。虽然复制zjblog只要一个小时就可以搞定一个网站,或者用cms系统,三四个小时就可以做出一个前后台都有的网站,而且想做成啥样也都行。但是就是要从新做,自己做的意义不一样,更何况,俺就是专门干这个的,嘿嘿嘿要做一个网站,而且从零开始,首先呢就是技术选型了,经过一番思量决定选择-SpringBoot做后端,前端使用Vue做一
JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)
跳房子的前端
前端面试 javascript 开发语言 ecmascript
在JavaScript中,深拷贝(DeepCopy)和浅拷贝(ShallowCopy)是用于复制对象或数组的两种不同方法。了解它们的区别和应用场景对于避免潜在的bugs和高效地处理数据非常重要。以下是对深拷贝和浅拷贝的详细解释,包括它们的概念、用途、优缺点以及实现方式。1.浅拷贝(ShallowCopy)概念定义:浅拷贝是指创建一个新的对象或数组,其中包含了原对象或数组的基本数据类型的值和对引用数
JAVA·一个简单的登录窗口
MortalTom
java 开发语言 学习
文章目录概要整体架构流程技术名词解释技术细节资源概要JavaSwing是Java基础类库的一部分,主要用于开发图形用户界面(GUI)程序整体架构流程新建项目,导入sql.jar包(链接放在了文末),编译项目并运行技术名词解释一、特点丰富的组件提供了多种可视化组件,如按钮(JButton)、文本框(JTextField)、标签(JLabel)、下拉列表(JComboBox)等,可以满足不同的界面设计
Spring MVC 全面指南:从入门到精通的详细解析
一杯梅子酱
技术栈学习 spring mvc java
引言:SpringMVC,作为Spring框架的一个重要模块,为构建Web应用提供了强大的功能和灵活性。无论是初学者还是有一定经验的开发者,掌握SpringMVC都将显著提升你的Web开发技能。本文旨在为初学者提供一个全面且易于理解的学习路径,通过详细的知识点分析和实际案例,帮助你快速上手SpringMVC,让学习过程既深刻又高效。一、SpringMVC简介1.1什么是SpringMVC?Spri
Spring Boot中实现跨域请求
BABA8891
spring boot 后端 java
在SpringBoot中实现跨域请求(CORS,Cross-OriginResourceSharing)可以通过多种方式,以下是几种常见的方法:1.使用@CrossOrigin注解在SpringBoot中,你可以在控制器或者具体的请求处理方法上使用@CrossOrigin注解来允许跨域请求。在控制器上应用:importorg.springframework.web.bind.annotation.
WebMagic:强大的Java爬虫框架解析与实战
Aaron_945
Java java 爬虫 开发语言
文章目录引言官网链接WebMagic原理概述基础使用1.添加依赖2.编写PageProcessor高级使用1.自定义Pipeline2.分布式抓取优点结论引言在大数据时代,网络爬虫作为数据收集的重要工具,扮演着不可或缺的角色。Java作为一门广泛使用的编程语言,在爬虫开发领域也有其独特的优势。WebMagic是一个开源的Java爬虫框架,它提供了简单灵活的API,支持多线程、分布式抓取,以及丰富的
博客网站制作教程
2401_85194651
java maven
首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java
解线性方程组
qiuwanchi
package gaodai.matrix;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Sc
在mysql内部存储代码
annan211
性能 mysql 存储过程 触发器
在mysql内部存储代码
在mysql内部存储代码,既有优点也有缺点,而且有人倡导有人反对。
先看优点:
1 她在服务器内部执行,离数据最近,另外在服务器上执行还可以节省带宽和网络延迟。
2 这是一种代码重用。可以方便的统一业务规则,保证某些行为的一致性,所以也可以提供一定的安全性。
3 可以简化代码的维护和版本更新。
4 可以帮助提升安全,比如提供更细
Android使用Asynchronous Http Client完成登录保存cookie的问题
hotsunshine
android
Asynchronous Http Client是android中非常好的异步请求工具
除了异步之外还有很多封装比如json的处理,cookie的处理
引用
Persistent Cookie Storage with PersistentCookieStore
This library also includes a PersistentCookieStore whi
java面试题
Array_06
java 面试
java面试题
第一,谈谈final, finally, finalize的区别。
final-修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能
网站加速
oloz
网站加速
前序:本人菜鸟,此文研究总结来源于互联网上的资料,大牛请勿喷!本人虚心学习,多指教.
1、减小网页体积的大小,尽量采用div+css模式,尽量避免复杂的页面结构,能简约就简约。
2、采用Gzip对网页进行压缩;
GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNⅨ系统的文件压缩。我们在Linux中经常会用到后缀为.gz
正确书写单例模式
随意而生
java 设计模式 单例
单例模式算是设计模式中最容易理解,也是最容易手写代码的模式了吧。但是其中的坑却不少,所以也常作为面试题来考。本文主要对几种单例写法的整理,并分析其优缺点。很多都是一些老生常谈的问题,但如果你不知道如何创建一个线程安全的单例,不知道什么是双检锁,那这篇文章可能会帮助到你。
懒汉式,线程不安全
当被问到要实现一个单例模式时,很多人的第一反应是写出如下的代码,包括教科书上也是这样
单例模式
香水浓
java
懒汉 调用getInstance方法时实例化
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(null == ins
安装Apache问题:系统找不到指定的文件 No installed service named "Apache2"
AdyZhang
apache http server
安装Apache问题:系统找不到指定的文件 No installed service named "Apache2"
每次到这一步都很小心防它的端口冲突问题,结果,特意留出来的80端口就是不能用,烦。
解决方法确保几处:
1、停止IIS启动
2、把端口80改成其它 (譬如90,800,,,什么数字都好)
3、防火墙(关掉试试)
在运行处输入 cmd 回车,转到apa
如何在android 文件选择器中选择多个图片或者视频?
aijuans
android
我的android app有这样的需求,在进行照片和视频上传的时候,需要一次性的从照片/视频库选择多条进行上传
但是android原生态的sdk中,只能一个一个的进行选择和上传。
我想知道是否有其他的android上传库可以解决这个问题,提供一个多选的功能,可以使checkbox之类的,一次选择多个 处理方法
官方的图片选择器(但是不支持所有版本的androi,只支持API Level
mysql中查询生日提醒的日期相关的sql
baalwolf
mysql
SELECT sysid,user_name,birthday,listid,userhead_50,CONCAT(YEAR(CURDATE()),DATE_FORMAT(birthday,'-%m-%d')),CURDATE(), dayofyear( CONCAT(YEAR(CURDATE()),DATE_FORMAT(birthday,'-%m-%d')))-dayofyear(
MongoDB索引文件破坏后导致查询错误的问题
BigBird2012
mongodb
问题描述:
MongoDB在非正常情况下关闭时,可能会导致索引文件破坏,造成数据在更新时没有反映到索引上。
解决方案:
使用脚本,重建MongoDB所有表的索引。
var names = db.getCollectionNames();
for( var i in names ){
var name = names[i];
print(name);
Javascript Promise
bijian1013
JavaScript Promise
Parse JavaScript SDK现在提供了支持大多数异步方法的兼容jquery的Promises模式,那么这意味着什么呢,读完下文你就了解了。
一.认识Promises
“Promises”代表着在javascript程序里下一个伟大的范式,但是理解他们为什么如此伟大不是件简
[Zookeeper学习笔记九]Zookeeper源代码分析之Zookeeper构造过程
bit1129
zookeeper
Zookeeper重载了几个构造函数,其中构造者可以提供参数最多,可定制性最多的构造函数是
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolea
【Java命令三】jstack
bit1129
jstack
jstack是用于获得当前运行的Java程序所有的线程的运行情况(thread dump),不同于jmap用于获得memory dump
[hadoop@hadoop sbin]$ jstack
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F
jboss 5.1启停脚本 动静分离部署
ronin47
以前启动jboss,往各种xml配置文件,现只要运行一句脚本即可。start nohup sh /**/run.sh -c servicename -b ip -g clustername -u broatcast jboss.messaging.ServerPeerID=int -Djboss.service.binding.set=p
UI之如何打磨设计能力?
brotherlamp
UI ui教程 ui自学 ui资料 ui视频
在越来越拥挤的初创企业世界里,视觉设计的重要性往往可以与杀手级用户体验比肩。在许多情况下,尤其对于 Web 初创企业而言,这两者都是不可或缺的。前不久我们在《右脑革命:别学编程了,学艺术吧》中也曾发出过重视设计的呼吁。如何才能提高初创企业的设计能力呢?以下是 9 位创始人的体会。
1.找到自己的方式
如果你是设计师,要想提高技能可以去设计博客和展示好设计的网站如D-lists或
三色旗算法
bylijinnan
java 算法
import java.util.Arrays;
/**
问题:
假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,
您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳
子上进行这个动作,而且一次只能调换两个旗子。
网上的解法大多类似:
在一条绳子上移动,在程式中也就意味只能使用一个阵列,而不使用其它的阵列来
警告:No configuration found for the specified action: \'s
chiangfai
configuration
1.index.jsp页面form标签未指定namespace属性。
<!--index.jsp代码-->
<%@taglib prefix="s" uri="/struts-tags"%>
...
<s:form action="submit" method="post"&g
redis -- hash_max_zipmap_entries设置过大有问题
chenchao051
redis hash
使用redis时为了使用hash追求更高的内存使用率,我们一般都用hash结构,并且有时候会把hash_max_zipmap_entries这个值设置的很大,很多资料也推荐设置到1000,默认设置为了512,但是这里有个坑
#define ZIPMAP_BIGLEN 254
#define ZIPMAP_END 255
/* Return th
select into outfile access deny问题
daizj
mysql txt 导出数据到文件
本文转自:http://hatemysql.com/2010/06/29/select-into-outfile-access-deny%E9%97%AE%E9%A2%98/
为应用建立了rnd的帐号,专门为他们查询线上数据库用的,当然,只有他们上了生产网络以后才能连上数据库,安全方面我们还是很注意的,呵呵。
授权的语句如下:
grant select on armory.* to rn
phpexcel导出excel表简单入门示例
dcj3sjt126com
PHP Excel phpexcel
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
if (PHP_SAPI == 'cli')
die('This example should only be run from a Web Brows
美国电影超短200句
dcj3sjt126com
电影
1. I see. 我明白了。2. I quit! 我不干了!3. Let go! 放手!4. Me too. 我也是。5. My god! 天哪!6. No way! 不行!7. Come on. 来吧(赶快)8. Hold on. 等一等。9. I agree。 我同意。10. Not bad. 还不错。11. Not yet. 还没。12. See you. 再见。13. Shut up!
Java访问远程服务
dyy_gusi
httpclient webservice get post
随着webService的崛起,我们开始中会越来越多的使用到访问远程webService服务。当然对于不同的webService框架一般都有自己的client包供使用,但是如果使用webService框架自己的client包,那么必然需要在自己的代码中引入它的包,如果同时调运了多个不同框架的webService,那么就需要同时引入多个不同的clien
Maven的settings.xml配置
geeksun
settings.xml
settings.xml是Maven的配置文件,下面解释一下其中的配置含义:
settings.xml存在于两个地方:
1.安装的地方:$M2_HOME/conf/settings.xml
2.用户的目录:${user.home}/.m2/settings.xml
前者又被叫做全局配置,后者被称为用户配置。如果两者都存在,它们的内容将被合并,并且用户范围的settings.xml优先。
ubuntu的init与系统服务设置
hongtoushizi
ubuntu
转载自:
http://iysm.net/?p=178 init
Init是位于/sbin/init的一个程序,它是在linux下,在系统启动过程中,初始化所有的设备驱动程序和数据结构等之后,由内核启动的一个用户级程序,并由此init程序进而完成系统的启动过程。
ubuntu与传统的linux略有不同,使用upstart完成系统的启动,但表面上仍维持init程序的形式。
运行
跟我学Nginx+Lua开发目录贴
jinnianshilongnian
nginx lua
使用Nginx+Lua开发近一年的时间,学习和实践了一些Nginx+Lua开发的架构,为了让更多人使用Nginx+Lua架构开发,利用春节期间总结了一份基本的学习教程,希望对大家有用。也欢迎谈探讨学习一些经验。
目录
第一章 安装Nginx+Lua开发环境
第二章 Nginx+Lua开发入门
第三章 Redis/SSDB+Twemproxy安装与使用
第四章 L
php位运算符注意事项
home198979
位运算 PHP &
$a = $b = $c = 0;
$a & $b = 1;
$b | $c = 1
问a,b,c最终为多少?
当看到这题时,我犯了一个低级错误,误 以为位运算符会改变变量的值。所以得出结果是1 1 0
但是位运算符是不会改变变量的值的,例如:
$a=1;$b=2;
$a&$b;
这样a,b的值不会有任何改变
Linux shell数组建立和使用技巧
pda158
linux
1.数组定义 [chengmo@centos5 ~]$ a=(1 2 3 4 5) [chengmo@centos5 ~]$ echo $a 1 一对括号表示是数组,数组元素用“空格”符号分割开。
2.数组读取与赋值 得到长度: [chengmo@centos5 ~]$ echo ${#a[@]} 5 用${#数组名[@或
hotspot源码(JDK7)
ol_beta
java HotSpot jvm
源码结构图,方便理解:
├─agent Serviceab
Oracle基本事务和ForAll执行批量DML练习
vipbooks
oracle sql
基本事务的使用:
从账户一的余额中转100到账户二的余额中去,如果账户二不存在或账户一中的余额不足100则整笔交易回滚
select * from account;
-- 创建一张账户表
create table account(
-- 账户ID
id number(3) not null,
-- 账户名称
nam