【Java】開発上の注意事項

序文

この記事は、私の経験と日々の開発における注意点を共有することを目的としています。この記事を通じて、読者が開発プロセスにおけるさまざまな問題にうまく対処できるようにし、いくつかの提案と解決策を提供したいと考えています。

1. Long型変換に注意

たとえば、フロントエンドには String 型の ID が必要ですが、データベースには Long 型が保存されています。確かに ModelToVo 中に Long 型を String に変換することもできますが、手順が冗長で内部的に使用するのに不便です。Long 型を直接使用する方が良いでしょう。

public class AiCreationRecordVo {
    
    

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

拡張機能: String 型 ID がフロントエンドに返され、現在のエンドが get リクエストを呼び出し、String 型 ID が queryParam として渡される場合、それを受け取るために String 型を使用する必要はなく、Long 型を使用して直接受け取るだけです。その主な理由は、プロパティを変換できる Spring 独自の TypeConverterDelegate です。

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

同様に、dto クラスの Long 型には次のような特別な処理が必要ですか。

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

逆シリアル化コードは次のとおりです。

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");
        }
    }
}

フロントエンドパラメータは次のとおりです。

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

@JsonDeserialize を追加しない場合でも、処理用の fastxml が背後にあるため、コードは通常どおり実行できます。

最初は com.fasterxml.jackson.databind.deser.BeanDeserializer ファイルの deserialize メソッドで、次に com.fasterxml.jackson.databind.deser.impl.MethodProperty ファイルの deserializeAndSet メソッドを呼び出します。ここでは String を Long に変換し、最後に com.fasterxml.jackson.databind.deser.std.NumberDeserializers ファイルの内部実装クラス L を呼び出します。 ongDeserializer には、デシリアライズ メソッドがあります。
シリアル化の問題は基本的に fastxml によって解決されることがわかり、ループ インターフェイス呼び出しテストの後、両者の所要時間はほぼ同じであることがわかります。

2. Swagger 注釈の使用

コントローラー層は Swagger アノテーションを使用し、リクエスト本文も

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

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

@ApiModel
public class CreationRecordQueryDto extends BaseQueryDto {
    
    

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

場合によっては、dto のフィールドはフロントエンドでは必要ないため、swagger 内に隠されます。

@ApiModelProperty(hidden = true)
private String ip;

3. Serializableの使用について

すべての dto、model、および vo は Serializable を実装する必要がありますか? 答えは必ずしもそうではありません。

シリアル化の主な目的は、ネットワーク上でオブジェクトを転送したり、ファイル システム、データベース、メモリにオブジェクトを保存したりすることです。

シリアル化が必要な場合:

  • メモリ内のオブジェクトの状態をファイルまたはデータベースに保存する場合。
  • ソケットを使用してネットワーク上でオブジェクトを転送する場合は、通常の単純なフロントエンドとバックエンドのデータ対話ではなく、readObject や writeObject を含む ObjectInputStream オブジェクトと ObjectOutputStream オブジェクトが関与することに注意してください。コード例はこの記事を参照してください。
  • RMIを介してオブジェクトを転送したい場合も同様に実装が複雑で、普段触ることはほとんどありませんので、この記事を読むことをお勧めします。

要約すると、Model はデータベースを処理する必要があるため、Serializable を実装する必要があります。dto と vo はこのインターフェイスを実装する必要はありません。

Spring がデフォルトで使用する json 解析ツールは jackson であるため、Serializable とは関係ありません。
場合によっては、シリアル化が実装されていない場合でも、データベースに永続化することができます。

実際、シリアル化された Date、String などのエンティティ クラスで一般的に使用されるデータ型を確認できます。また、一部の基本型には、データベース内に対応するデータ構造があります。クラス宣言からは、serializabel インターフェイスが実装されていません。実際、さまざまな変数を宣言するとき、特定のデータ型はシリアル化操作を実現するのに役立ちます。

4. 春事利用について

通常、パラメータの検証はサービス層で処理しますが、メソッドがたまたまトランザクションを開始し、パラメータの検証が失敗してリターンした場合、このトランザクションは無駄になるでしょうか? 答えは「ノー」です。次の理由からです。

@Transactional アノテーションは、トランザクションを開始する方法です。Spring で @Transactional アノテーションを使用すると、プロキシ オブジェクトが Bean として作成されます。プロキシ オブジェクトのメソッドを呼び出すと、最初に @Transactional アノテーションがメソッドに追加されているかどうかが判断されます。追加された場合は、トランザクション マネージャーを使用してデータベース接続を作成し、データを変更します。

Spring フレームワークは、TransactionInterceptor クラスを通じて MySQL データベースのトランザクションと結合されます。TransactionInterceptor は、Spring トランザクション インフラストラクチャ org.springframework.transaction.PlatformTransactionManager を使用して、宣言型トランザクション管理のために Spring フレームワークに組み込まれた MethodInterceptor です。

MySQLデータベースを基に解析を行っており、@Transcationalアノテーションを付加した上でこのメソッドに入るとbeginまたはstarttransactionを実行したことと同等となり、対応するcommit文がcommit、rollback文がrollbackとなります。begin/starttransaction コマンドはトランザクションの開始点ではありません。トランザクションは、実行後の InnoDB テーブルを操作する最初のステートメントの後に実際に開始されます。

要約すると、サービスメソッドにパラメータ検証を追加しても、パフォーマンスには影響しません。逆に、Spring では、トランザクションを任意にオープンすると、パフォーマンス上の問題が発生する可能性があります。これは、トランザクションによってデータベース ロックが発生し、パフォーマンスに影響を与えるためです。また、トランザクションのスコープが予想よりも大きい場合、パフォーマンスの問題が発生する可能性があります。

アプリケーションシナリオ:

  • 一度に 1 つのクエリ ステートメントを実行する場合、トランザクション サポートを有効にする必要はなく、データベースは SQL 実行中の読み取り一貫性をデフォルトでサポートします。
  • このシナリオでは、統計クエリやレポート クエリなどの複数のクエリ ステートメントを一度に実行する場合、複数のクエリ SQL で全体的な読み取りの一貫性を確保する必要があります。そうでないと、前の SQL クエリの後と 2 番目の SQL クエリの前にデータが他のユーザーによって変更され、今回の全体的な統計クエリと読み取りデータが矛盾しているように見えます。このとき、トランザクション サポートを有効にする必要があります。

特定の情報をカウントするために複数のクエリが一度に実行されることに注意してください。このとき、データの全体的な一貫性を確保するために、読み取り専用トランザクションが使用されます。@Transcational(readOnly=true)

5、MapとJsonの変換に注意する

Map が Json 文字列に変換されると、別の Map に追加されます。新しい Map を Json 文字列形式に変換する必要がある場合、変換後、内部 Map から変換された Json 文字列にエスケープ文字「\」が追加されます。

 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);
    }

実行結果は次のとおりです。

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

この場合、マップオブジェクトをStringに変換せずに直接設定できないか検討してください。

6. MybatisPlus XMLファイルの注意事項

6.1、整数クエリ

クエリ条件が Integer 型の場合、値が 0 の場合、以下の判定は false になります。

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

テストの結果、mybatis の if は 0 を ' ' とみなしていることが判明したため、この判定は条件に入ることはできませんが、数値を 0 以外に変更するだけで十分です。次のように変更します

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

ソースコードによると、mybatis は SQL のプリコンパイル時に if タグを解析するために OGNL 式を使用しています。
整数型属性の場合、age != '' など、'' に等しくない場合、OGNL は '' の長さを返します。 ソースコード: (s.length() == 0) ? age が 0 の場合、if 条件は失敗します。

6.2. ブールクエリ

本来の書き方:

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

ただし、Payd が false の場合、上記の判断は無効となり、真のデータが引き続き照会されます。
次のように書くことを検討してください。

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

要約する

経験と知識を共有することで、共に進歩できると確信しています。あなたが初心者でも、ベテランでも、これらの経験の要約が何らかの助けとインスピレーションを提供できることを願っています。

おすすめ

転載: blog.csdn.net/u011397981/article/details/131805322