• lemon

    在生活中,会遇到两种问题,一种是无法解决的问题,一种是可以解决的问题。

    可以解决的问题又可以分为两种,一种是看得到的,一种是看不到的。

    看得到的又可以分为两种,一种是看到当做没看到的,也就是装睡的,一种是看到也当做看到的

    当然还可以继续分下去,但是今天我只想讲讲装睡的故事。


    小明是一名高中生,他非常的喜欢编程,因为这样要他感受到了自己与其他高中生与众不同,要自己有了一种满足感。这个时候他发现一个问题,这个问题就是:他喜欢编程,但是他又要高考,但是高考目前比他编程重要太多了。

    这个时候正确的做法应该是开始准备高考,但是小明却没有这么做,他这么安慰自己。编程需要用到英语,需要用到数学,同时还需要分析,如果我继续编程下去,数学、英语都是没有问题的,我可以拿数学和英语的时间用来编程。

    然后,第二天小明到了学校,在上英语课和数学课的时候,发现根本就不是这么回事,这三者之间的差距巨大。小明想了想,他并没有接受现实,而是继续的安慰自己,如果我提高一下自己的编程方向,将编程和数学和英语进一步结合,一定是可以的。

    随着时间过去,小明打脸了,虽然他一定程度的将数学和英语结合到一起,但是他们的深度是不能和考试要求的深度相提并论。这个时候小明是清楚的问题的,但是小明还是继续装睡。于是他又开始安慰自己,我就算不读大学,只要将自己的编程能力发挥到极限,高中毕业也能要自己有一片天地。

    随着时间的进一步过去,转眼间只有3个月就要高考了,他的考试成绩一塌糊涂,他安慰自己的编程能力也是一塌糊涂。小明当然是知道这个问题的,但是小明太能装睡了,它又如此的安慰自己,高考考的好又能怎么样,还不是给别人打死工,还不如在编程上拼一拼,虽然这个时候小明自己都不太相信自己想的。但是他还是相信了自己的都不太相信的话。

    最后高考成绩下来了,小明一点都不伤心,一点也不难过,因为他自己一直都明白,自己的问题在哪里,自己要做什么,但是他却在一直骗自己。

  • lemon

    想必很多人都看了刚才的那篇文章,觉得写一个controller的测试好麻烦啊,其实不是的,那是因为我还没有讲完........(o(╥﹏╥)o 你坑我)

    好了,现在来说说正确的写法的,当然有很多种,这里直说一种

    @AutoConfigureMockMvc

    @SpringBootTest

    class IndexViewTest {

        @Autowired
    private lateinit var mockMvc: MockMvc

    }

    好了,是不是突然觉得,这才是spring 嘛,刚才的哪些是什么鬼嘛

    @WebMvcTest(IndexViewAction::class)

    @SpringBootTest

    class IndexViewTest {
    @Autowired
    private lateinit var mockMvc: MockMvc

    }

    上面这个是只测试web层


    注意事项

    关于@Test包的导入问题,如果你导入的是import org.junit.Test包中的,必须要加上下面这句

    @RunWith(SpringRunner::class)

    如果这个的话import org.junit.jupiter.api.Test,则不需要上面这句

    注意,如果使用maven的test指令没有反应,那是因为你是用的是import org.junit.Test中的Test,换成org.junit.jupiter.api.Test就好了,或者修改maven文件

    <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>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>


  • lemon

    如果你需要测试的控制器有forward,请看看这里,了解如何测试


    使用集成web环境进行测试,请使用如下方法创建MockMvc

    @Autowired private lateinit var indexViewAction: indexViewAction
    private lateinit var mockMvc: MockMvc

    @Before
    fun before(){
    mockMvc = MockMvcBuilders
    .standaloneSetup(indexViewAction)
    .build()
    }

    注入你需要的controller,然后将其放入standaloneSetup


    这种是使用独立测试方法进行

    首先,我们先准备一个要做单元测试的类,更详细的在post和get的带方法测试上,可以直接翻到最后

    @RunWith(SpringRunner::class)
    /*@SpringBootTest(classes = [Application::class]) 需要配外的配置*/
    @SpringBootTest /* 不需要格外的配置 */
    class IndexViewTest {

    private lateinit var mockMvc: MockMvc
    @Autowired
    private lateinit var webApplicationContext: WebApplicationContext

    @Before
    fun init(){
    /*创建一个mock mvc*/
    mockMvc = MockMvcBuilders
    .webAppContextSetup(webApplicationContext)
    .build()
    }

    }


    想必大家看到了MockMvcBuilders,这个类是用来帮助我们创建一个MockMvc的,name什么是MockMvc了

    // 这个是MockMvc的文档描述,提供spring mvc的测试支持

    /**
    * <strong>Main entry point for server-side Spring MVC test support.</strong>
    *
    * <h3>Example</h3>
    *
    * <pre >
    * import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    * import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    * import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
    *
    * // ...
    *
    * WebApplicationContext wac = ...;
    *
    * MockMvc mockMvc = webAppContextSetup(wac).build();
    *
    * mockMvc.perform(get("/form"))
    * .andExpect(status().isOk())
    * .andExpect(content().mimeType("text/html"))
    * .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
    * </pre>
    *
    * @author Rossen Stoyanchev
    * @author Rob Winch
    * @author Sam Brannen
    * @since 3.2
    */

    现在我们可以开始测试了,先来简单的测试一个get post方法

    @RunWith(SpringRunner::class)
    /*@SpringBootTest(classes = [Application::class]) 需要配外的配置*/
    @SpringBootTest /* 不需要格外的配置 */
    class IndexViewTest {

    private lateinit var mockMvc: MockMvc
    @Autowired
    private lateinit var webApplicationContext: WebApplicationContext

    @Before
    fun init(){
    /*创建一个mock mvc*/
    mockMvc = MockMvcBuilders
    .webAppContextSetup(webApplicationContext)
    .build()
    }

    /*使用get测试*/
    @Test
    fun get(){
    mockMvc
    .get("/index.html") /*指定get方法要使用的url地址*/
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    /*使用post测试*/
    @Test
    fun post(){
    mockMvc
    .post("/index.html") /*指定get方法要使用的url地址*/
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    }

    但是很多时候,我们的测试哪里有这么简单,一般都是带参数的,那么带参数要如何测试呢

    重点,这个才是重点

    @RunWith(SpringRunner::class)
    /*@SpringBootTest(classes = [Application::class]) 需要配外的配置*/
    @SpringBootTest /* 不需要格外的配置 */
    class IndexViewTest {

    private lateinit var mockMvc: MockMvc
    @Autowired
    private lateinit var webApplicationContext: WebApplicationContext

    @Before
    fun init(){
    /*创建一个mock mvc*/
    mockMvc = MockMvcBuilders
    .webAppContextSetup(webApplicationContext)
    .build()
    }

    /*使用get测试*/
    @Test
    fun get(){
    mockMvc
    .get("/index.html") /*指定get方法要使用的url地址*/
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    /*使用post测试*/
    @Test
    fun post(){
    mockMvc
    .post("/index.html") /*指定get方法要使用的url地址*/
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    /*带参数的get测试*/
    @Test
    fun getAndParam(){
    mockMvc
    .perform(
    MockMvcRequestBuilders.get("/index.html")/*设置url地址*/
    .param("page","1")/*设置参数*/
    .cookie(Cookie("name","lemon"))/*设置cookie*/
    .header("username","lemon")/*设置请求头*/
    /*当然还可以设置很多东西,按.就可以看到了*/
    )
    .andExpect ( /*设置成功条件*/
    matchAll( /* 这是一个静态方法,它的返回值刚好就是andExpect需要的参数*/
    status().isOk, /*这是静态方法,判断返回类型*/
    header().exists("Content-Type"), /*这也是静态方法,判断头里面是不是有指定的头,只包含你自己添加的头,不包括默认头(也就是哪些不是你设置的头)*/
    content().encoding("utf-8") /*判断返回的编码*/
    )
    )
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    /*带参数的post测试*/
    @Test
    fun postAndParam(){
    mockMvc
    .perform(
    MockMvcRequestBuilders.post("/index.html")/*设置url地址*/
    .param("page","1")/*设置参数*/
    .cookie(Cookie("name","lemon"))/*设置cookie*/
    .header("username","lemon")/*设置请求头*/
    /*当然还可以设置很多东西,按.就可以看到了*/
    )
    .andExpect ( /*设置成功条件*/
    matchAll( /* 这是一个静态方法,它的返回值刚好就是andExpect需要的参数*/
    status().isOk, /*这是静态方法,判断返回类型*/
    header().exists("Content-Type"), /*这也是静态方法,判断头里面是不是有指定的头,只包含你自己添加的头,不包括默认头(也就是哪些不是你设置的头)*/
    content().encoding("utf-8") /*判断返回的编码*/
    )
    )
    .andReturn() /*返回结果*/
    .response /*返回内容*/
    .contentAsString /*内容作为html形式*/
    .apply(::println) /*打印结果*/
    }

    }

    注意注意,如果你要是报错了之后再来看 的话,更加要注意

    import org.springframework.test.web.servlet.ResultMatcher.matchAll
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*

    这些东西一定要引入


    spring boot的接口测试进一步说明

  • lemon

    拼接文本

    <span th:text="'The name of the user is ' + ${user.name}">

    一般的拼接方式

    推荐的拼接方式

    <span th:text="|Welcome to our application, ${user.name}|">


    拼接链接地址

    拼接参数

    <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
    <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

    参数不是在尾部怎么用

    <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

    更加复杂的链接地址拼接

    <a th:href="@{${url}(orderId=${o.id})}">view</a>
    <a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>


  • lemon

    首先,先创建一张表

    create table people(
    id int primary key,
    name nvarchar(128) not null
    )

    配置对应的依赖文件

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.remonkado</groupId>
    <artifactId>jpa-transcational-study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.BUILD-SNAPSHOT</version>
    </parent>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    </dependencies>

    </project>

    添加application.yml

    spring:
    datasource:
    url: jdbc:mysql://127.0.0.1:3306/study?useSSL=false
    password: 123456
    username: lemon
    driver-class-name: com.mysql.cj.jdbc.Driver
    jpa:
    show-sql: true

    对应的实体

    @Entity
    @Table(name = "people")
    public class People {

    @Id
    public Integer id;

    @Column
    public String name;

    public Integer getId() {
    return id;
    }

    public void setId(Integer id) {
    this.id = id;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }
    }

    JPA仓库

    public interface PeopleMapper extends JpaRepository<People,Integer> {
    }

    和一个main方法

    @SpringBootApplication
    public class Application {

    public static void main(String[] args){
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    }

    }


    首先我们先来插入一条数据

    public static void main(String[] args){
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    PeopleMapper bean = context.getBean(PeopleMapper.class);
    People people = new People();
    people.id = 1;
    people.name = "lemon";
    bean.save(people);
    people.name = "o no";
    }


    看到语句了,插入成功了

    Hibernate: select people0_.id as id1_0_0_, people0_.name as name2_0_0_ from people people0_ where people0_.id=?
    Hibernate: insert into people (name, id) values (?, ?)

    想必大家也看到了,在我的后面有一个people.name = "0 no"; 这是在我们没有启用事务的情况下


    接下来我们改写一个Application这个类,因为事务是不能直接在方法里面用的嘛

    @SpringBootApplication
    public class Application {

    @Autowired
    public PeopleMapper peopleMapper;

    @Transactional
    public void test(){
    People people = new People();
    people.id = 1;
    people.name = "lemon";
    peopleMapper.save(people);
    people.name = "o no";
    }

    public static void main(String[] args){
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    Application bean = context.getBean(Application.class);
    bean.test();
    }

    }

    运行,想必各位以为会报错,应为主键冲突了,再一次保存的话,但是结果出乎意料,它没有报错,而是输出了一条这个的语句

    Hibernate: select people0_.id as id1_0_0_, people0_.name as name2_0_0_ from people people0_ where people0_.id=?

    和大家想的一样,后面那一条people.name = "o no";依旧是没什么用

    我们改变一下代码,将save返回的返回值接受起来,在改一改这个返回值的东西

    @SpringBootApplication
    public class Application {

    @Autowired
    public PeopleMapper peopleMapper;

    @Transactional
    public void test(){
    People people = new People();
    people.id = 33;
    people.name = "lemon";
    People result = peopleMapper.save(people);
    result.name = "o no";
    }

    public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    Application bean = context.getBean(Application.class);
    bean.test();

    }

    }

    执行一下

    Hibernate: select people0_.id as id1_0_0_, people0_.name as name2_0_0_ from people people0_ where people0_.id=?
    Hibernate: insert into people (name, id) values (?, ?)
    Hibernate: update people set name=? where id=?

    什么,后面那一条有了效果,看来save的那个返回值是一个持久化对象,我们只要修改它的值,它的修改会自动的对应到数据库里面的数据

    现在我们再来看看除了事务,是不是还有这个效果

    @SpringBootApplication
    public class Application {

    @Autowired
    public PeopleMapper peopleMapper;

    @Transactional
    public People test(){
    People people = new People();
    people.id = 34;
    people.name = "lemon";
    People result = peopleMapper.save(people);
    return result;
    }

    public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    Application bean = context.getBean(Application.class);
    bean.test().name = "100";
    }

    }

    结果

    Hibernate: select people0_.id as id1_0_0_, people0_.name as name2_0_0_ from people people0_ where people0_.id=?
    Hibernate: insert into people (name, id) values (?, ?)

    没有更新操作,看来出了事务是没有效果的

    那么去掉事务试试

    @SpringBootApplication
    public class Application {

    @Autowired
    public PeopleMapper peopleMapper;

    public People test(){
    People people = new People();
    people.id = 35;
    people.name = "lemon";
    People result = peopleMapper.save(people);
    return result;
    }

    public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    Application bean = context.getBean(Application.class);
    bean.test().name = "100";
    }

    }

    可能大家以为这一次在外面也会更新,但是你们猜错了

    Hibernate: select people0_.id as id1_0_0_, people0_.name as name2_0_0_ from people people0_ where people0_.id=?
    Hibernate: insert into people (name, id) values (?, ?)

    并没有



    通过以上几个测试,JPA的save方法在开始事务,而且在事务没有关闭的时候,我们修改它返回的对象里面的数据,会自动更新到数据库


  • ucl

    博主牛批!!!!

  • lemon


    今天,有个人问我,这个是什么警告了,其实我一看并不是很理解,不过看到 "../",就要我觉得是URL注入漏洞的攻击

    然后我根据它发过来的图片,找到了打印这个的类

    • org.springframework.web.servlet.resource.ResourceHttpRequestHandler

    这是一个Spring的资源请求类,我们来看一看它提示这个警告的地方

    /**
    * Identifies invalid resource paths. By default rejects:
    * <ul>
    * <li>Paths that contain "WEB-INF" or "META-INF"
    * <li>Paths that contain "../" after a call to
    * {@link org.springframework.util.StringUtils#cleanPath}.
    * <li>Paths that represent a {@link org.springframework.util.ResourceUtils#isUrl
    * valid URL} or would represent one after the leading slash is removed.
    * </ul>
    * <p><strong>Note:</strong> this method assumes that leading, duplicate '/'
    * or control characters (e.g. white space) have been trimmed so that the
    * path starts predictably with a single '/' or does not have one.
    * @param path the path to validate
    * @return {@code true} if the path is invalid, {@code false} otherwise
    * @since 3.0.6
    */
    protected boolean isInvalidPath(String path) {
    if (path.contains("WEB-INF") || path.contains("META-INF")) {
    if (logger.isWarnEnabled()) {
    logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
    }
    return true;
    }
    if (path.contains(":/")) {
    String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
    if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
    if (logger.isWarnEnabled()) {
    logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
    }
    return true;
    }
    }
    if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
    if (logger.isWarnEnabled()) {
    logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
    }
    return true;
    }
    return false;
    }

    看它这个方法名,想必各位直接就懂了吧

    检查是不是无效的路径,而且这些路径有些还是非法路径,比如这个WEB-INF目录,是用来存放特殊文件的地方,是不能直接被访问的


    其实这个URL注入是什么原理呢??

    比如有如下一段代码

    public class URLAttack {

    @Test
    public void attack() throws MalformedURLException {
    URL url = new URL("https://www.baidu.com/download?fileName=../Action.java");
    String query = url.getQuery();
    System.out.println(query); // fileName=../Action.java
    String filename = query.split("=")[1];
    System.out.println(filename); // ../Action.java
    File location = new File("/data/temp/",filename);
    System.out.println(location.toPath()); // \data\temp\..\Action.java
    }

    }

    你会发现文件的路径变成了 \data\temp\..\Action.java 等价 \data\Action.java

    而我们只想它能访问temp目录下的文件,现在它可以访问data目录下的文件,越过了访问权限


    Paths that contain "WEB-INF" or "META-INF"
    Paths that contain "../" after a call to StringUtils.cleanPath(java.lang.String).
    Paths that represent a valid URL or would represent one after the leading slash is removed.

    这个类是用来处理静态资源的,而在不拦截这些特殊字符情况下,是可以进行URL注入达到任意读取文件的目的(当然咯,Spring已经帮我们把这个问题解决了)

    注意: 这个只有Spring配置的静态资源的那一部分才检测,并不是说所有的请求都检测


    我看到这个有个槽点

    static../manage.py

    assets../assets/app.js

    怎么都是py和js结尾的啊

  • lemon

    想必各位都看过这个数据库吧,一直以来都不知道有什么用???

    当然,有个时候是不会显示这个数据库的,如果你没有看到这个数据库,请执行以下SQL

    show databases;

    你会惊讶的发现,什么,我隐藏了一个数据库嘛,没错,你的想法是对的,这个数据库里面存储的数据就是MySQL的配置


    我们在看一看里面都有一些什么表的

    use mysql;
    show tables;

    我们看到里面有好多好多的表


    但只有这张user表示我们的重点,要我们看看里面都有一些什么字段吧


    字段多的数不完,呃呃,这就很难受了,不过不要紧,今天的主角只有Host字段这一个重点,我们来查询一下我们root账户的Host字段都有一些什么吧。

    select host from user where user = 'root';


    我们发现里面的值是localhost,这说明我们只能用localhost和127.0.0.1来登录数据库

    这时候,我们把它改一下

    update user set host = '%' where user = 'root';

    然后我就遇到了一个这样的错误


    怎么办了,好解决,我们临时性的关闭安全删除

    SET SQL_SAFE_UPDATES = 0;

    执行完这条SQL后,就运行刚才那条SQL,你会发现就成功了。

    这个时候我们就可以远程访问了,但是注意的是,Host设置成%后,就不能用localhost和127.0.0.1了(网友们讲的,我没试验过)



    那么问题来了,Host字段可以设置成其他的值吗,可以的,比如你只想要192.168.1.12这个IP可以远程访问这个数据库,那么就将Host字段设置成192.168.1.12

    如果你想要192.168.1.*的可以远程访问,那么就将Host字段改成192.168.1.%,看到这里,想必小伙伴们就清楚了,其实%就是通配符的意思


    好了,如果想了解Host字段的跟多信息,拿起你的百度吧,他们写的比我的详细,有的还比我的更加准确


  • lemon

    这是一个很诡异的问题,在启动的时候不报错,把各个功能都试试也不报错,但是过一段时间就报错

    Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@????(No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.  

    然后看了看错误信息,发现是hikari连接池的问题,然后在网上看了看网友帮助,发现是原因好像是因为,在某些原因下,tcp连接断开了,但是程序不知道啊。为了应对这个问题,hikari连接池每过一段时间会检查一下连接是否断开,默认是1800000毫秒,,呃呃,这怎么能行啊,在网络差的情况下,超大概率连接池全部连接都断开了也不会重新检查一次啊,这实在是太久了吧

    并且这个参数也是用来设置一个连接的最大存在时间,如果这个连接建立的时间超过了设定的值,也会被关闭,移除线程池

    然后我们只要在appliction.yml下添加如下配置

    spring.datasource.hikari.max-lifetime =30000

    添加了这个配置就不会出问题了吗??其实不是的,如果你的TCP连接断开频率太高,也就是你的网络太差,依然会出这个错误

    为什么在这里不设置成1000毫秒了,因为小于30000毫秒,设置是无效的,这这这,也太骚了吧(小于这个值,自动回来默认值1800000毫秒


    是不是以为万事大吉了,添加了这一行配置,你运行起来,立刻获得一条警告

    idleTimeout is close to or more than maxLifetime, disabling it

    为什么会有这一条警告呢?

    这个因为idleTimeout的默认值是600000,而我们的maxLifetime的值现在是30000

    呃呃,这个参数是用来配置一个连接最多空闲多长时间,并且这个空闲时间不能小于maxLifettime,如果小于,则禁用此功能,并且这个值不能小于 10000 毫秒

    然后我们在添加一行配置,当然也可以不添加

    spring.datasource.hikar.idle-timeout: 20000


    设置完之后,一样的你会获得一个警告

     idleTimeout has been set but has no effect because the pool is operating as a fixed size pool.

    这个怎么办呢,修改这两个参数

    spring.datasource.hikar.maximum-pool-size = 32
    spring.datasource.hikar.minimum-idle = 16

    使得它不是固定大小的线程池,当然看max和min应该就懂了o(╥﹏╥)o


    注意:以上设置的值均为演示设置的值,请根据具体需要自行设置


  • lemon

    (_Д_)啊啊啊啊啊啊,今天突然发现自己把自己彻底玩炸了

    今日复明日,明日何其多。∑ (´△`)?!

    啊啊啊啊,上天啊,能不能要我保留记忆的给我倒流一波时间啊。不用太多


    倒流但我18岁的时候就好了

  • lemon
    事情是这样的,今天我将从数据库实体里面查出来的实体进行了字段修改,结果JPA自动的修改了数据库里面的值

    发生了这样的事情,我百思不得其解,为什么今天发生了这样的事情,而之前却没有遇到呢

    随后,我开启了自己的面向搜索引擎编程。

    在网上,我了解到JPA的对象有三种状态瞬时态(Transient),持久态(Persistent)和脱管态(Detached),有兴趣的小伙伴可以去搜一下,但这个不是我的重点,因为这个我之前就知道,我想知道的是,为什么我之前没有这个问题,现在却有这个问题呢??


    其实,这个问题是因为transcational启动了,但是没有提交,或者是事务无效了。事务无效其实也并不能马上发现错误,因为我们放进去的实体进行修改之后,JPA也是不会自动保存的


    在什么时候JPA会自动保存了,只有我们在开启了事务的情况下,并且使用的是JPA返回的代理对象,而且修改了里面的值(在transcational没有提交之前),才会自动保存

  • lemon

    相信很多小伙伴都遇到过这个错误,但是有个时候却怎么都找不出问题在哪里。

    我们来看一下下面这个例子
    public class Action{
    @GetMapping("/good")
    public String good(){
    return "good";
    }
    @GetMapping("/nice")
    public String nice(){
    return "/good";
    }
    }


    请问,这两个方法有什么区别呢,聪明的小伙伴一下就看出来了,区别在于返回值上,有一个已/开头,一个直接开头。

    这个写法在window下,是没有区别的。

    但是在linux下,就会出现Error resolving template [??????], template might not exist or might not be accessible by any of the configured Template Resolvers的错误

    看到这里,可能就会有小伙伴疑惑了,这是什么鬼啊。

    /good是绝对路径 good是相对路径  

    • 最新发表 12