待完成:
nexus 本地仓库,上传下载jar包
jwt解析
基于 Spring Security和 JWT的权限系统设计
https://www.codesheep.cn/2019/03/14/security-jwt-hibernate/
表定义,数据库表迁移、更新
https://flywaydb.org/getstarted/firststeps/maven#creating-the-first-migration
字段唯一,unique_together
unique_together = (('type_name', 'capability_name'))
查询:
精确(UserCopy1Example.andDescriptionEqualTo) done
模糊(UserCopy1Example.andDescriptionLike) done
条件排序(UserCopy1Example.orderByClause) done
枚举:done
Java 枚举(enum) 详解7种常见的用法
https://blog.csdn.net/qq_27093465/article/details/52180865
MyBaits中枚举类型——在数据库字段类型为字符串、数字等非枚举类型情况下的使用
https://blog.csdn.net/houyefeng/article/details/45886613
MyBatis数据库字段映射Java枚举
https://blog.csdn.net/chidi5109/article/details/100599083
Mybatis枚举映射数据库int存储转换
https://blog.csdn.net/zangdaiyang1991/article/details/86518088
SpringBoot整合Mybatis实现自动转换枚举类型
https://blog.csdn.net/qq_26440803/article/details/83451221
SpringBoot与MybatisPlus3.X整合之通用枚举(十二)
https://www.cnblogs.com/dalianpai/p/11771269.html
路由管理、请求处理
@Temporal(TemporalType.TIMESTAMP)
@Column
@JsonProperty("create_time")
@JsonDeserialize(using = EventCreateTimeDeserializer.class)
@JsonSerialize(using = EventCreateTimeSerializer.class)
@TableField(typeHandler = EventCreateTimeHandler.class)
@Future(message = "时间在当前时间之后才可以通过")
private java.util.Date createTime;
post http://localhost:9000/log/
EventCreateTimeDeserializer.deserialize
Entity:
setCreateTime
setLevel
@Future(message = "时间在当前时间之后才可以通过")
private java.util.Date createTime;
getEffectiveTemporalValidationTolerance
FutureValidatorForDate.getInstant
AbstractFutureInstantBasedValidator.is_valid
EventLevelValidator
long id = this.getBaseMapper().insert(event);
getCreateTime
[2020-02-13 14:57:42.646] [DEBUG] <http-nio-9000-exec-1> org.apache.ibatis.logging.jdbc.BaseJdbcLogger:143 ==> Preparing: INSERT INTO tianshu_log_event ( create_time, content, level, owner, description ) VALUES ( ?, ?, ?, ?, ? )
EventCreateTimeHandler.setParameter
EventLevelHandler.setParameter
return this.getById(event.getId());
setCreateTime、setLevel
EventCreateTimeSerializer.serialize
controller(RestController),model(Entity、Domain),view
小马哥:第6章 Web MVC REST 应用
https://coding.imooc.com/class/chapter/252.html#Anchor
数据校验:
快速入手 Spring Boot 参数校验
Spring Boot 参数校验(@Validated和@Valid的区别、自定义校验、如何使用嵌套校验)
https://blog.csdn.net/Howinfun/article/details/90287047
validation-api包校验嵌套属性(集合对象)的写法
https://blog.csdn.net/huangdi1309/article/details/89673875
序列化和反序列化
小马哥:第6章 Web MVC REST 应用
https://coding.imooc.com/class/chapter/252.html#Anchor/
https://spring.io/guides/gs/accessing-data-mysql/
CRUD
外键关联(一对一,多对一,多对多,外键删除行为:CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET):
分页(userCopy1Repository.getPageList(page, pageSize),
PagingAndSortingRepository)
https://github.com/pagehelper/Mybatis-PageHelper
批量操作、删除 (SimpleJpaRepository)
第八节 Java Persistence API (课件) (注意:此处只是实现功能,由于jpa做了很多封装,考虑性能的话还是要用mybatis)
原子操作 ?
.setCommit(false)
.setCommit(true)
https://segmentfault.com/n/1330000009887617
自定义response
https://blog.csdn.net/shenszy/article/details/85676415
httpClient
RestTemplate
createTime--> create_time
异步
http://www.spring4all.com/article/540
未实现,遇到问题:
spring boot2 EnableAysnc If the controller requires proxying (e.g. due to @Transactional), please us
打包
1 jar 命令行运行
java -jar target\msgcenter-0.0.1-SNAPSHOT.jar
2 war tmocat容器中运行,设置如如下:
部署
docker
http://www.spring4all.com/article/557
application.yml
# Logger Config
logging:
level:
root: info # 全局
ik.starriver.msgcenter: debug # 包级别
pattern:
console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){magenta} %clr([%5p]) %highlight(%C:%L) %m %n'
file: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){magenta} %clr([%5p]) %highlight(%C:%L) %m %n'
# 默认日志文件名
file: test.log
# 默认日志路径
path: ./log
# logback.xml路径,默认为classpath:logback.xml
# config: ./logback.xml
#app
server:
port: 9000
#spring
spring:
output:
ansi:
enabled: ALWAYS
devtools:
restart:
enabled: false
# mysql DATABASE CONFIG
datasource:
# 使用druid数据源
# type: com.alibaba.druid.pool.DruidDataSource
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://10.1.252.23:3306/springtest?serverTimezone=UTC
username: root
password: 123456
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
# dbcp2
dbcp2:
url: jdbc:mysql://10.1.252.23:3306/springtest?serverTimezone=UTC
username: root
password: '%tBqnWF1y3ku'
maxTotal: 20
maxIdle: 5
driverClassName: com.mysql.cj.jdbc.Driver
jpa:
open-in-view: false
database: MYSQL
databasePlatform: org.hibernate.dialect.MySQL5InnoDBDialect
generateDdl: true
hibernate:
ddlAuto: update
#mybatis
mybatis-plus:
mapper-locations: classpath:/mapper*/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: ik.starriver.msgcenter.entity
# typeEnumsPackage: com.baomidou.springboot.entity.enums
global-config:
# 数据库相关配置
db-config:
# table-prefix: tianshu_log_
#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
# id-type: ASSIGN_ID
#字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
field-strategy: not_empty
#驼峰下划线转换
column-underline: true
#数据库大写下划线转换
#capital-mode: true
#逻辑删除配置
logic-delete-value: 0
logic-not-delete-value: 1
db-type: mysql
#刷新mapper 调试神器
refresh: true
# 原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ik.starriver</groupId>
<artifactId>msgcenter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>msgcenter</name>
<description>msgcenter project for starriver</description>
<properties>
<java.version>1.8</java.version>
<mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
<docker.image.prefix>starriver</docker.image.prefix>
<!-- <dockerfile.maven.version>1.3.4</dockerfile.maven.version>-->
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mybatis-plus start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis-plus end -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
打包:
D:\shen\java\webdevelop\spring\msgcenter>java -jar target\msgcenter-0.0.1-SNAPSHOT.jar
上传jar包到部署环境:
Dockerfile
命令行方式:docker run -e "JAVA_OPTS=-Xmx52m" app-image
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD msgcenter-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 9000
ENV JAVA_OPTS="-Xmx52m"
ENV DATABASE_URL="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springtest"
ENV DATABASE_USERNAME="--spring.datasource.username=root"
ENV DATABASE_PASSWORD="--spring.datasource.password=123456"
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $DATABASE_URL $DATABASE_USERNAME $DATABASE_PASSWORD
# 启动命令模式为:exec java $JAVA_OPTS(jvm的参数) xxx-app.jar $SPRING_OPTIONS(spring的参数)
docker-compose.yml
[root@shen msgcenter]# cat docker-compose.yml
version: "3"
services:
web:
build: .
image: msgcenter
ports: ['9000:9000']
#command: ["/bin/bash", "start.sh"]
# command: ["uwsgi", "--ini", "uwsgi.ini"]
#environment:
# - ALLOW_HOST=*
#- DB_HOST=10.1.252.23
#- DB_PORT=3306
#- DB_USER=root
#- DB_PASSWORD=%tBqnWF1y3ku
#- DB_NAME=tianshu_data_dict
# volumes: ['.:/code']
docker-deploy.py
[root@shen msgcenter]# cat docker-deploy.py
import re
import os
DOCKER_COMPOSE_IMAGE_NAME = "msgcenter"
def stop_container():
p = os.popen('docker ps')
output = p.read()
# print(output)
pattern = re.compile(".*{}".format(DOCKER_COMPOSE_IMAGE_NAME))
try:
container_id = re.search(pattern, output).group().split()[0]
if not container_id:
return
p = os.popen('docker stop {}'.format(container_id))
output = p.read()
print("docker stop {}".format(output))
except (AttributeError, ) as e:
print("stop_container: {}".format(str(e)))
def docker_compose():
p = os.popen('docker-compose up -d --build')
output = p.read()
print(output)
def docker_ps():
print("------------------")
docker_ps_cmd = 'docker ps|grep {}'.format(DOCKER_COMPOSE_IMAGE_NAME)
print(docker_ps_cmd)
p = os.popen(docker_ps_cmd)
output = p.read()
print(output)
if __name__ == "__main__":
stop_container()
docker_compose()
docker_ps()
生成 image,并启动docker
[root@shen msgcenter]# python3 docker-deploy.py
PS:
cli动态传入全局变量的方式:
D:\shen\java\webdevelop\spring\log>java -jar target\log-0.0.1-SNAPSHOT.jar
--spring.datasource.url=
jdbc:mysql://10.1.252.23:3306/springisp?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf8
--spring.profiles.active=pro
--spring.datasource.username=root
--spring.datasource.password=123456
官网部署文档
https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#containers-deployment
FROM openjdk:8-jdk-alpine AS builder
WORKDIR target/dependency
ARG APPJAR=target/*.jar
COPY ${APPJAR} app.jar
RUN jar -xf ./app.jar
FROM openjdk:8-jre-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.MyApplication"]
数据库时区问题
JDBC 与 MySQL 的这种误解,导致出现了数据库实际存储的时间戳,与业务系统取到的值差了 13 或者 14 个小时,这个时间与美中是(=13)否(=14)采用夏令时息息相关。
解决:
mysql> set global time_zone = '+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> set time_zone = '+08:00';
Query OK, 0 rows affected (0.00 sec)
https://blog.csdn.net/qq_30553235/article/details/79612824
restdocs
https://www.cnblogs.com/qq350760546/p/8397557.html
https://docs.spring.io/spring-restdocs/docs/2.0.4.RELEASE/reference/html5/#introduction
http://www.spring4all.com/article/3333
http请求构造类:
RestTemplate
@Test
public void test() {
final String url = "http://localhost:9000/log/?page=2&pageSize=2&orderBy=level&direction=desc&searchName=content&searchValue=con&nameLike=true";
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
requestHeaders.add("Authorization", "Bearer test");
RestTemplate template = new RestTemplate();
HttpEntity<String> requestEntity = new HttpEntity<String>(null, requestHeaders);
ResponseEntity<String> response = template.exchange(url, HttpMethod.GET, requestEntity, String.class);
String result = response.getBody();
System.err.println(result);
}
MockMvc
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs(uriPort = 9000)
public class RestControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetEventPage() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.put("Content-Type", Arrays.asList(MediaType.APPLICATION_JSON_VALUE));
headers.put("Authorization", Arrays.asList("Bearer dXNlcjpzZWNyZXQ="));
this.mockMvc.perform(get("/log/?page=2&pageSize=2&orderBy=level&direction=desc&searchName=content&searchValue=con&nameLike=true").headers(headers))
.andExpect(status().isOk())
.andDo(document("eventPage",
requestHeaders(
headerWithName("Content-Type").description(
"desc Content-Type"),
headerWithName("Authorization").description(
"JWT token: Bearer dXNlcjpzZWNyZXQ=")),
requestParameters(
parameterWithName("page").description("The page to retrieve"),
parameterWithName("pageSize").description("Entries per page"),
parameterWithName("orderBy").description("Entries orderBy"),
parameterWithName("direction").description("Entries orderBy direction"),
parameterWithName("searchName").description("filter by column name"),
parameterWithName("searchValue").description("filter by column name's value"),
parameterWithName("nameLike").description("use name like or not")
)
// responseHeaders(
// headerWithName("X-RateLimit-Reset").description(
// "Time at which the rate limit period will reset"))
));
}