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.promeggroupId >
< artifactId> tinypinyinartifactId >
< version> 2.0.3version >
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.mapstructgroupId >
< artifactId> mapstructartifactId >
< version> ${map.struct.version}version >
< scope> providedscope >
dependency >
< dependency>
< groupId> org.mapstructgroupId >
< artifactId> mapstruct-processorartifactId >
< version> ${map.struct.version}version >
< scope> providedscope >
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.whvcsegroupId >
< artifactId> easy-captchaartifactId >
< version> 1.6.2version >
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.bootgroupId >
< artifactId> spring-boot-starter-testartifactId >
< scope> testscope >
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)
微信小程序中数值计算的精度丢失问题
WorkAndDebugger
微信小程序 微信小程序
在微信小程序中,当你遇到数值计算的精度丢失问题时,主要是因为JavaScript在处理浮点数时存在固有的精度问题。这是因为计算机内部使用二进制形式存储数字,而某些十进制小数在二进制中无法精确表示,从而导致了精度误差。解决这个问题的方法有几个:使用整数运算:如果你的应用允许,可以将所有涉及的小数转换为整数再进行计算。例如,将货币值乘以100转换为分进行计算,最后再除以100转换回元。function
java 接口创建对象
专注API从业者
经验分享 电商平台 API接口 java 开发语言 大数据 数据库 数据挖掘
Java接口创建对象:深度解析与示例在Java编程中,接口是一种非常重要的概念。它不仅为不同类之间的交互提供了规范,也促进了代码的重用和解耦。本文将深入探讨如何在Java中使用接口创建对象,带您理解这一关键特性。什么是接口?接口(Interface)是一种特殊的抽象类,定义了一组方法的声明,但不提供具体的实现。接口允许我们定义类的方法,而不需要指定它们是什么。这使得接口在Java中成为实现多态性、
Ai+若依(集成easyexcel实现excel表格增强)
潇洒(sa)一点
java
EasyExcel介绍官方地址:EasyExcel官方文档-基于Java的Excel处理工具|EasyExcel官网Java解析、生成Excel比较有名的框架有Apachepoi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大
【drools】文档2:起步 drools和dmn
等风来不如迎风去
网络服务入门与实战 spring drools
GettingStartedDroolsUserGuide8.44.0.FinalGettingStartedFirstRuleProjectThisguidewalksyouthroughtheprocessofcreatingasimpleDroolsapplicationproject.PrerequisitesJDK11+withJAVA_HOMEconfiguredappropriate
Java集合:Stack详解
重生之我要进大厂
Java 基础 java 开发语言
Stack详解基本概念主要方法基本操作集合操作使用示例注意事项替代方案Java中的Stack类是一个后进先出(LIFO,LastInFirstOut)的数据结构,它继承自Vector类。尽管Stack是一个可用的类,但它被认为是遗留的,并且在新代码中不推荐使用。通常建议使用Deque或ArrayDeque作为替代。基本概念后进先出:意味着最后一个添加到Stack中的元素将是第一个被移除的元素。继承
ReactiveRedisTemplate自动配置定义和序列化方式选择spring-data-redis
简放视野
Spring-Boot 工作实战总结 Redis spring boot java redis
背景Spring-Boot的Redis自动配置类,RedisReactiveAutoConfiguration和RedisAutoConfiguration,组件ReactiveRedisTemplate和RedisTemplate默认使用JDK序列化方式,在现实业务场景中很难使用,其存储的值可读性差且又长。我觉得不是很合理,意味着使用它们的用户都需要自己重新自定义。ReactiveStringR
Python字符串的切片运算操作
MG_鹏鹏
Python学习笔记 python 开发语言
Python语言的字符串切片运算功能类比于Java等语言的字符串截取函数要简洁方使很多,使用起来很是顺手。一、语法格式:Python对字符的中的元素或者子串的访问使用方括号[],类似于Java中的数组,但又青出于蓝而胜于蓝,通过索引值(下标)和:符灵活配合。格式:变量名[始下标:终下标:步长]其中始下标可以是0即从左往左开始,也可以是-1从右往左开始详见下图:从左往右01234567从右往左-8-
基于Java毕业设计新锐台球厅管理系统源码+系统+mysql+lw文档+部署软件
练练科技
java 课程设计 mysql
基于Java毕业设计新锐台球厅管理系统源码+系统+mysql+lw文档+部署软件基于Java毕业设计新锐台球厅管理系统源码+系统+mysql+lw文档+部署软件本源码技术栈:项目架构:B/S架构开发语言:Java语言开发软件:ideaeclipse前端技术:Layui、HTML、CSS、JS、JQuery等技术后端技术:JAVA运行环境:Win10、JDK1.8数据库:MySQL5.7/8.0源码
export default vs module.exports:JavaScript 模块导出的双雄对决
正小安
vue杂谈 前端 javascript ecmascript
exportdefaultvsmodule.exports:JavaScript模块导出的双雄对决在JavaScript的模块化编程中,exportdefault和module.exports是两种不可或缺的语法,它们分别代表了ES6模块系统(也称为ES2015模块或ES6模块)和CommonJS模块系统的核心导出机制。了解它们之间的区别与联系,对于编写可维护、跨平台兼容的JavaScript代码
SprinBoot+Vue远程教育网站的设计与实现
赵广陆
project vue.js 前端 javascript
目录1项目介绍2项目截图3核心代码3.1Controller3.2Service3.3Dao3.4application.yml3.5SpringbootApplication3.5Vue4数据库表设计5文档参考6计算机毕设选题推荐7源码获取1项目介绍博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w+粉丝,超300w访问量,专注于大学生项目实战开发、讲解和答疑辅导
Maven坐标查找方法及Maven-Search 插件的使用(保姆级教学)
几分醉意.
IDEA插件 Maven maven java intellij-idea
目录坐标的概念获取坐标方法一获取坐标方法二坐标的概念坐标组成是groupid,artifiactld,version。坐标概念来自数学。坐标作用:确定资源的,是资源的唯一标识。在maven中,每个资源都有坐标。坐标值是唯一的。简称叫gav。拿servlet依赖举例javax.servlet<
Java基础——深入理解Java中的多线程(超级详细,值得你看)
2401_84558508
作者\/ java 开发语言
开发中优先选择实现Runnable方式原因:实现的方式没有类的单继承性的局限性实现的方式更适合来处理多个线程共享数据的情况相同点:都需要重写run(),将线程要执行的代码声明在run()方法中线程的生命周期解决线程安全问题;使用同步代码块packagecom.haust.java;/*出现线程安全问他的解决方法:当一个线程a在操作ticket的时候,其他线程不能参与进来,直到线程参与进来,也操作完
用JavaScript写一个简单的9*9乘法表
迷雾yx
javascript 开发语言 ecmascript
Documentspan{width:80px;padding:5px10px;display:inline-block;/*可以使行内元素表现得像块级元素一样*/text-align:center;/*文本居中*/border:1pxsolidturquoise;margin:2px;/*每个边框之间距离为2px*/border-radius:10px;/*边框圆润*/box-shadow:6p
How can I load the openai api configuration through js in html?
营赢盈英
AI ai python javascript html node.js openai
题意:怎样在HTML中通过JavaScript加载OpenAIAPI配置问题背景:Iamtryingtosendarequestthroughjsinmyhtmlsothatopenaianalyzesitandsendsaresponse,butifinthejsIputthefollowing:我正在尝试通过HTML中的JavaScript发送一个请求,以便让OpenAI分析它并发送响应,但如
Spring Cloud Gateway:现代微服务架构中的API网关解决方案
鹿又笑
架构 微服务 spring cloud gateway
引言在微服务架构中,API网关作为客户端与后端服务之间的中介,承担着请求路由、负载均衡、认证授权等重要职责。SpringCloudGateway是Spring生态系统中的一款强大且灵活的API网关解决方案,本文将深入探讨其工作原理、核心功能及应用场景。什么是SpringCloudGateway?SpringCloudGateway是SpringCloud项目的一部分,旨在为微服务架构提供高效的AP
jmeter远程启动配置
雪霁_
性能测试 压力测试 jmeter linux
前提条件保持windows端、linux端jdk版本一致配置linux压力机(配置agent)修改文件jmeter.properties,打开注释server_port开启端口(默认1099);打开注释,修改server.rmi.ssl.disable=false为true运行命令,./jmeter-server-Djava.rmi.server.hostname=10.0.0.48(ip为lin
eureka核心操作解析:集群间数据同步、自我保护、三级缓存
dejavu111
eureka java
1.什么是注册中心?Eureka是springcloud中的一个负责服务注册与发现的组件。遵循着CAP理论中的A(可用性)P(分区容错性)。一个Eureka中分为eurekaserver和eurekaclient。其中eurekaserver是作为服务的注册与发现中心。eurekaclient既可以作为服务的生产者,又可以作为服务的消费者。具体结构如下图:Eureka是一个基于REST(表述性状态
mac java 8卸载_[macOS]macOS上卸载Java9 重装Java8
V6火箭
mac java 8卸载
[TOC]0x00场景装了Java9,但是发现不少应用不支持,没办法,卸载。0x01探索查看java版本java--versionorjava-version查看Java路径whichjava显示结果:/usr/bin/java。进入目录/usr/bin/,定位到原始位置/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands。
java计算机毕业设计高校学生资助管理信息系统源码+mysql数据库+系统+lw文档+部署
鸿源网络
数据库 mysql java
java计算机毕业设计高校学生资助管理信息系统源码+mysql数据库+系统+lw文档+部署java计算机毕业设计高校学生资助管理信息系统源码+mysql数据库+系统+lw文档+部署本源码技术栈:项目架构:B/S架构开发语言:Java语言开发软件:ideaeclipse前端技术:Layui、HTML、CSS、JS、JQuery等技术后端技术:JAVA运行环境:Win10、JDK1.8数据库:MySQ
Pixel2刷入原生Google系统并获取ROOT权限
Devil丶LY
移动端 android
一、设备信息机型:pixel2二、预刷系统系统:google原生系统(pixel对应版本为walleye)Android版本:11Magisk版本:23.0(root用)三、刷机工具fastbootadb四、下载网站Google原生系统镜像下载地址TWRP下载地址Magisk下载地址五、刷机文件walleye-rp1a.201005.004.a1-factory-0c23f6cf.zip(goog
分享JavaScript中直接调用CSS中的类名
奔跑的呱呱牛
javascript css 开发语言
分享JavaScript中直接调用CSS中的类名在现代的JavaScript框架(如React、Vue)中,使用CSS模块(CSSModules)是一种非常流行的方式。.module.css文件扩展名代表的是CSS模块,它与普通的CSS文件不同,能够解决样式全局污染的问题,并且实现更好的样式隔离。为什么要使用.module.css作用域隔离:传统的CSS文件中的类名是全局的,这意味着如果多个组件或
从头开始学习->JVM(一):java内存模型(JMM)不是java虚拟机内存模型(JVM)哦!
吴里斯蒂亚诺_翀纳尔多
前言在进入jvm的世界之前,我要先给大家整理一个概念上的细节,java内存模型和jvm内存模型,是两回事哦。在网上很多朋友的博客上,都会无意识的将java内存模型和jvm内存模型混为一谈,这让一部分不明白真相的看客都会以为是这样,但是实际上java内存模型和jvm内存模型,在概念上来讲是两回事,当然,不能否认的是这两者有着极大的关联性,甚至某种意义上来讲,JVM是JMM的实现方式,JMM是为JVM
mac上安装和卸载java的jdk
默然相爱鱼鱼鱼
jdk
mac上安装和卸载java的jdkbrew安装brewcaskinstalljava8#安装java81配置Java环境变量1、定位JAVA_HOME位置/usr/libexec/java_home12、编辑profile文件vim~/.bash_profile1格式参考JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Con
LinkedList模拟出栈入栈
虾米大王
Java java windows 开发语言
packagecom.shrimpking.t11;importjava.util.LinkedList;/***CreatedbyIntelliJIDEA.**@Author:Shrimpking*@create2024/9/1011:49*/publicclassMyStack{privateLinkedListlist=newLinkedListms=newMyStack<>();ms.pu
Java 正则表达式
南风_001
正则表达式定义了字符串的模式。正则表达式可以用来搜索、编辑或处理文本。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。正则表达式实例一个字符串其实就是一个简单的正则表达式,例如HelloWorld正则表达式匹配"HelloWorld"字符串。.(点号)也是一个正则表达式,它匹配任何一个字符如:"a"或"1"。下表列出了一些正则表达式的实例及描述:正则表达式描述thisistext匹配
庖丁解java(一篇文章学java)
庖丁解java
java 开发语言
(大家不用收藏这篇文章,因为这篇文章会经常更新,也就是删除后重发)一篇文章学java,这是我滴一个执念...当然,真一篇文章就写完java基础,java架构,java业务实现,java业务扩展,根本不可能.所以,这篇文章,就是一个索引,索什么呢?请看下文.关于决定开始写博文的介绍(一切故事的起点源于这一次反省)中小技术公司的软扩展(微服务扩展是否有必要?)-CSDN博客SpringCloud(一个
基于JAVA高校毕业就业管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
源新网络
java 开发语言
基于JAVA高校毕业就业管理系统计算机毕业设计源码+数据库+lw文档+系统+部署基于JAVA高校毕业就业管理系统计算机毕业设计源码+数据库+lw文档+系统+部署本源码技术栈:项目架构:B/S架构开发语言:Java语言开发软件:ideaeclipse前端技术:Layui、HTML、CSS、JS、JQuery等技术后端技术:JAVA运行环境:Win10、JDK1.8数据库:MySQL5.7/8.0源码
如何用Kotlin实现MVP架构的Android快速开发框架?
2401_84132496
程序员 android kotlin 架构
下拉刷新和上拉加载更多空页面在断网情况下加载缓存网络请求错误并重新加载基于Kotlin的使用说明(Java的使用具体参考java版本的demo)导入依赖在根目录中的build.gradle文件中加入maven{url“https://jitpack.io”}在应用下的build.gradle文件中加入在自定义的Application类中的onCreate方法中初始化CommonLibrary//初
Java 获取中文拼音和首字母
颇有几分姿色
java基础 java
需求:获取中文首字母当做用户登录名。获取用户姓名拼音做其他逻辑。不需要定制化使用hutool封装的PinyinUtil即可,我这里需要自己定制,所以写了个工具类。实现基于搜狐的Pinyin4j库。依赖:cn.hutoolhutool-all5.4.2com.belerwebpinyin4j2.5.1PinyinUtils代码示例PinyinUtils提供了以下主要方法:toHanYuPinyinS
面试频繁被问Java中的各种锁,今天就给大伙来梳理梳理外加视频教学
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