[Java] Matters needing attention in the development process

foreword

This article aims to share my experience and precautions in daily development. Through this article, I hope to help readers better deal with various problems in the development process, and provide some suggestions and solutions.

1. Pay attention to Long type conversion

For example, the front-end needs a String type id, but the Long type is stored in the database. It is true that the Long type can be converted into a String during modelToVo, but the steps are redundant, and it is inconvenient to use internally. It is better to use the Long type directly.

public class AiCreationRecordVo {
    
    

    @JsonDeserialize(using = LongJsonDeserializer.class)
    @JsonSerialize(using = LongJsonSerializer.class)
    private Long recordId;
 }

Extension: If the String type id is returned to the front end, and the current end calls the get request, and the String type id is passed in as queryParam, then there is no need to use the String type to receive it, and just use the Long type to receive it directly. The core reason is Spring's own TypeConverterDelegate, which can convert properties.

@GetMapping("/info")
public ApiResult getInfo(@RequestParam Long id, HttpServletRequest request)

Similarly, does the Long type in the dto class need special handling, such as the following:

@JsonSerialize(using = LongJsonSerializer.class)
@JsonDeserialize(using = LongJsonDeserializer.class)
private Long articleId;

The deserialization code is as follows:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class LongJsonDeserializer extends JsonDeserializer<Long> {
    
    

    private static final Logger logger = LoggerFactory.getLogger(LongJsonDeserializer.class);

    @Override
    public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
    
    
        String value = jsonParser.getText();
        try {
    
    
            return value == null || value.length() == 0 ? null : Long.valueOf(value);
        } catch (NumberFormatException e) {
    
    
            logger.error("LongJsonDeserializer.deserialize failed:{},cause:{}", e.getMessage(), e.getCause());
            throw new RuntimeException("LongJsonDeserializer.deserialize failed");
        }
    }
}

The front-end parameters are as follows:

{
    
    
        "articleId":"1633271396750176257",
        "wordCount":"10"
}

If you don't add @JsonDeserialize, the code can also run normally, because there is fasterxml behind it for processing.

The first is the deserialize method of the com.fasterxml.jackson.databind.deser.BeanDeserializer file, and then call the deserializeAndSet method of the com.fasterxml.jackson.databind.deser.impl.MethodProperty file, because here we are converting String to Long, and finally call com.fasterxml.jackson.databind.deser.std.NumberDeserializers file internal implementation class L ongDeserializer, which has a deserialize method.
It can be found that the serialization problem is essentially solved through fasterxml, and after the loop interface call test, it is found that the time-consuming of the two is almost the same.

2. The use of swagger annotations

The controller layer uses swagger annotations, and the request body also uses

@Api(tags = "AI创作记录")

@ApiOperation(value = "用户生成论文记录分页查询")

@ApiModel
public class CreationRecordQueryDto extends BaseQueryDto {
    
    

    @ApiModelProperty(value = "用户id", hidden = true)
    private String userId;
 }

Sometimes the fields in dto are not required by the front end, so they are hidden in swagger.

@ApiModelProperty(hidden = true)
private String ip;

3. About the use of Serializable

Do all dto, model, and vo need to implement Serializable? The answer is not necessarily.

The main purpose of serialization is to transfer objects over the network or store objects in file systems, databases, and memory.

When serialization is required:

  • When you want to save the object state in memory to a file or database;
  • When you want to use sockets to transfer objects on the network; pay attention, it is not our usual simple front-end and back-end data interaction, it involves ObjectInputStream and ObjectOutputStream objects, including readObject and writeObject, code examples refer to this article.
  • When you want to transfer objects through RMI; the implementation is equally complicated, and we rarely touch it at ordinary times. It is recommended to read this article.

To sum up, Model must implement Serializable because it needs to deal with the database. dto and vo do not need to implement this interface.

The json parsing tool used by Spring by default is jackson, so it is not related to Serializable.
Sometimes serialization is not implemented, and it can still be persisted to the database:

In fact, we can look at the commonly used data types in entity classes, such as Date, String, etc., which have been serialized, and some basic types have corresponding data structures in the database. From our class declaration, we have not implemented the serializabel interface. In fact, when declaring various variables, the specific data type helps us realize the serialization operation.

4. About the use of Spring affairs

Under normal circumstances, we deal with the verification of parameters at the service layer. If the method happens to start a transaction, once the parameter verification fails and returns, will this transaction be wasted? The answer is no, for the following reasons:

The @Transactional annotation is a way to start a transaction. When you use @Transactional annotation in Spring, it creates a proxy object as a bean. When calling the method of the proxy object, it will first determine whether the @Transactional annotation is added to the method. If it is added, then use the transaction manager to create a database connection and modify the data.

The Spring framework is combined with the transactions of the MySQL database through the TransactionInterceptor class. TransactionInterceptor is a MethodInterceptor built into the Spring framework for declarative transaction management, using the Spring transaction infrastructure org.springframework.transaction.PlatformTransactionManager.

We analyze based on the MySQL database. After adding the @Transcational annotation, when entering this method, it is equivalent to executing begin or start transaction. The matching commit statement is commit, and the rollback statement is rollback. The begin/start transaction command is not the starting point of a transaction. The transaction is really started after the first statement that operates the InnoDB table after they are executed.

To sum up, even if the parameter verification is added to the service method, it will not affect the performance. Conversely, in Spring, if you open a transaction arbitrarily, it may cause performance problems. This is because transactions cause database locks, which impact performance. Also, if your transaction scope is larger than you expect, it can cause performance issues.

Application scenario:

  • If you execute a single query statement at a time, there is no need to enable transaction support, and the database supports read consistency during SQL execution by default;
  • If you execute multiple query statements at one time, such as statistical query and report query, in this scenario, the multiple query SQL must ensure the overall read consistency. Otherwise, after the previous SQL query and before the second SQL query, the data is changed by other users, and the overall statistical query of this time will appear inconsistent with the read data. At this time, transaction support should be enabled.

Note that multiple queries are performed at one time to count certain information. At this time, in order to ensure the overall consistency of the data, read-only transactions are used@Transcational(readOnly=true)

Five, pay attention to Map and Json conversion

When a Map is converted into a Json string, it is added to another Map. If the new Map needs to be converted into a Json string format, then after conversion, the Json string converted from the internal Map will be added with "\" escape characters.

 public static void main(String[] args) {
    
    
        HashMap<String, Object> param = new HashMap<>();
        param.put("userId", 66666);

        param.put("username", "XXXX");

        HashMap<String, String> pushMap = new HashMap<>();
        pushMap.put("testKey01", "value01");
        pushMap.put("testKey02", "value02");

        param.put("pushJson", JSON.toJSONString(pushMap));

        String pushJson = JSON.toJSONString(param);
        System.out.println(pushJson);
    }

The execution result is:

{
    
    
        "pushJson":
                "{\"testKey01\":\"value01\",\"testKey02\":\"value02\"}",
        "userId":66666,
        "username":"XXXX"
}

In this case, consider whether it is possible to directly set the map object without converting it to a String.

6. Precautions for MybatisPlus XML file

6.1, Integer query

When the query condition is of type Integer, if the value is 0, the following judgment is false.

<if test="chargeStatus != null and chargeStatus== 0 ">
    AND c.charge_status = #{chargeStatus}
</if>

After testing, it is found that the if of mybatis regards 0 as ' ', so this judgment cannot enter the condition, but it is enough to change the number to a non-zero; change it to
:

<if test="chargeStatus != null and chargeStatus== ‘0’ ">
    AND c.charge_status =  #{chargeStatus}
</if>

According to the source code, mybatis uses OGNL expressions to parse if tags when precompiling SQL.
For Integer type attributes, when judging that they are not equal to '', such as age != '', OGNL will return the length of ''. Source code: (s.length() == 0) ? When age is 0, the if condition fails to pass.

6.2. Boolean query

The original way of writing:

<if test="dto.paid != null and dto.paid != ''">
    and ur.is_paid = #{dto.paid}
</if>

However, the above judgment is invalid when paid is false, and true data will still be queried.
Consider writing this:

<choose>
    <when test="dto.paid">
        and ur.is_paid = 1
    </when>
    <when test="dto.paid != null and !dto.paid">
        and ur.is_paid = 0
    </when>
</choose>

Summarize

I am convinced that by sharing experience and knowledge, we can make progress together. Whether you are just starting out or a seasoned veteran, I hope these summaries of experience can provide you with some help and inspiration.

Guess you like

Origin blog.csdn.net/u011397981/article/details/131805322