4 Commits

Author SHA1 Message Date
8eac9054cd refactor(gson): 重构 JSR310TypeAdapters
- 抽象出 TemporalAccessorTypeAdapter 类,简化了 LocalDate、LocalDateTime、ZonedDateTime 和 Instant 类型适配器的实现
2025-06-09 17:55:25 +08:00
a55c712349 test: 使用 JSR310TypeAdapters 简化测试代码 2025-06-09 17:21:26 +08:00
0eda94a658 refactor(exception): 为异常类添加 serialVersionUID
为以下异常类添加 serialVersionUID 字段:
- ParsingFailureException
- BizException
- InvalidInputException
- RequestParamsException
- DataOperationResultException
- SysException
2025-06-09 17:05:10 +08:00
c816696c55 docs: 改正 ParsingFailureException 文档注释中的错误描述 2025-06-09 16:14:44 +08:00
11 changed files with 58 additions and 92 deletions

View File

@@ -78,6 +78,7 @@ System.out.println(result); // Output: Return string
public final class LoginException public final class LoginException
extends RuntimeException extends RuntimeException
implements MultiTypesException<LoginException, LoginException.Type> { implements MultiTypesException<LoginException, LoginException.Type> {
private static final long serialVersionUID = 881293090625085616L;
private final Type type; private final Type type;
private LoginException(@Nonnull Type type, @Nonnull String message) { private LoginException(@Nonnull Type type, @Nonnull String message) {
super(message); super(message);

View File

@@ -35,6 +35,7 @@ import xyz.zhouxy.plusone.commons.base.IWithCode;
* public final class LoginException * public final class LoginException
* extends RuntimeException * extends RuntimeException
* implements MultiTypesException&lt;LoginException, LoginException.Type&gt; { * implements MultiTypesException&lt;LoginException, LoginException.Type&gt; {
* private static final long serialVersionUID = 881293090625085616L;
* private final Type type; * private final Type type;
* private LoginException(&#64;Nonnull Type type, &#64;Nonnull String message) { * private LoginException(&#64;Nonnull Type type, &#64;Nonnull String message) {
* super(message); * super(message);

View File

@@ -31,7 +31,7 @@ import xyz.zhouxy.plusone.commons.exception.MultiTypesException.ExceptionType;
* 如果表示用户传参造成的解析失败,可使用 {@link RequestParamsException#RequestParamsException(Throwable)} * 如果表示用户传参造成的解析失败,可使用 {@link RequestParamsException#RequestParamsException(Throwable)}
* 将 ParsingFailureException 包装成 {@link RequestParamsException} 再抛出。 * 将 ParsingFailureException 包装成 {@link RequestParamsException} 再抛出。
* <pre> * <pre>
* throw new RequestParamsException(ParsingFailureException.of(ParsingFailureException.Type.NUMBER_PARSING_FAILURE)); * throw new RequestParamsException(ParsingFailureException.Type.NUMBER_PARSING_FAILURE.create());
* </pre> * </pre>
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
@@ -40,6 +40,7 @@ import xyz.zhouxy.plusone.commons.exception.MultiTypesException.ExceptionType;
public final class ParsingFailureException public final class ParsingFailureException
extends RuntimeException extends RuntimeException
implements MultiTypesException<ParsingFailureException, ParsingFailureException.Type> { implements MultiTypesException<ParsingFailureException, ParsingFailureException.Type> {
private static final long serialVersionUID = 795996090625132616L;
private final Type type; private final Type type;

View File

@@ -29,6 +29,7 @@ package xyz.zhouxy.plusone.commons.exception.business;
* @since 1.0.0 * @since 1.0.0
*/ */
public class BizException extends RuntimeException { public class BizException extends RuntimeException {
private static final long serialVersionUID = 982585090625482416L;
private static final String DEFAULT_MSG = "业务异常"; private static final String DEFAULT_MSG = "业务异常";

View File

@@ -36,6 +36,7 @@ import xyz.zhouxy.plusone.commons.exception.MultiTypesException;
public final class InvalidInputException public final class InvalidInputException
extends RequestParamsException extends RequestParamsException
implements MultiTypesException<InvalidInputException, InvalidInputException.Type> { implements MultiTypesException<InvalidInputException, InvalidInputException.Type> {
private static final long serialVersionUID = -28994090625082516L;
private final Type type; private final Type type;

View File

@@ -26,6 +26,7 @@ package xyz.zhouxy.plusone.commons.exception.business;
* @since 1.0.0 * @since 1.0.0
*/ */
public class RequestParamsException extends BizException { public class RequestParamsException extends BizException {
private static final long serialVersionUID = 448337090625192516L;
private static final String DEFAULT_MSG = "用户请求参数错误"; private static final String DEFAULT_MSG = "用户请求参数错误";

View File

@@ -31,6 +31,7 @@
* public final class LoginException * public final class LoginException
* extends RuntimeException * extends RuntimeException
* implements MultiTypesException&lt;LoginException, LoginException.Type&gt; { * implements MultiTypesException&lt;LoginException, LoginException.Type&gt; {
* private static final long serialVersionUID = 881293090625085616L;
* private final Type type; * private final Type type;
* private LoginException(&#64;Nonnull Type type, &#64;Nonnull String message) { * private LoginException(&#64;Nonnull Type type, &#64;Nonnull String message) {
* super(message); * super(message);

View File

@@ -31,6 +31,7 @@ package xyz.zhouxy.plusone.commons.exception.system;
* @since 1.0.0 * @since 1.0.0
*/ */
public final class DataOperationResultException extends SysException { public final class DataOperationResultException extends SysException {
private static final long serialVersionUID = 992754090625352516L;
private final long expected; private final long expected;
private final long actual; private final long actual;

View File

@@ -26,6 +26,7 @@ package xyz.zhouxy.plusone.commons.exception.system;
* @since 1.0.0 * @since 1.0.0
*/ */
public class SysException extends RuntimeException { public class SysException extends RuntimeException {
private static final long serialVersionUID = -936435090625482516L;
private static final String DEFAULT_MSG = "系统异常"; private static final String DEFAULT_MSG = "系统异常";

View File

@@ -21,6 +21,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
@@ -42,16 +44,15 @@ public class JSR310TypeAdapters {
* {@code LocalDate} 的 {@code TypeAdapter} * {@code LocalDate} 的 {@code TypeAdapter}
* 用于 Gson 对 {@code LocalDate} 进行相互转换。 * 用于 Gson 对 {@code LocalDate} 进行相互转换。
*/ */
public static final class LocalDateTypeAdapter extends TypeAdapter<LocalDate> { public static final class LocalDateTypeAdapter
extends TemporalAccessorTypeAdapter<LocalDate, LocalDateTypeAdapter> {
private final DateTimeFormatter dateTimeFormatter;
/** /**
* 默认构造函数, * 默认构造函数,
* 使用 {@link DateTimeFormatter#ISO_LOCAL_DATE} 进行 {@link LocalDate} 的序列化与反序列化。 * 使用 {@link DateTimeFormatter#ISO_LOCAL_DATE} 进行 {@link LocalDate} 的序列化与反序列化。
*/ */
public LocalDateTypeAdapter() { public LocalDateTypeAdapter() {
this.dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE; this(DateTimeFormatter.ISO_LOCAL_DATE);
} }
/** /**
@@ -61,20 +62,7 @@ public class JSR310TypeAdapters {
* @param formatter 用于序列化 {@link LocalDate} 的格式化器,不可为 {@code null}。 * @param formatter 用于序列化 {@link LocalDate} 的格式化器,不可为 {@code null}。
*/ */
public LocalDateTypeAdapter(DateTimeFormatter formatter) { public LocalDateTypeAdapter(DateTimeFormatter formatter) {
AssertTools.checkArgumentNotNull(formatter, "formatter can not be null."); super(LocalDate::from, formatter);
this.dateTimeFormatter = formatter;
}
/** {@inheritDoc} */
@Override
public void write(JsonWriter out, LocalDate value) throws IOException {
out.value(dateTimeFormatter.format(value));
}
/** {@inheritDoc} */
@Override
public LocalDate read(JsonReader in) throws IOException {
return LocalDate.parse(in.nextString(), dateTimeFormatter);
} }
} }
@@ -82,16 +70,15 @@ public class JSR310TypeAdapters {
* {@code LocalDateTime} 的 {@code TypeAdapter} * {@code LocalDateTime} 的 {@code TypeAdapter}
* 用于 Gson 对 {@code LocalDateTime} 进行相互转换。 * 用于 Gson 对 {@code LocalDateTime} 进行相互转换。
*/ */
public static final class LocalDateTimeTypeAdapter extends TypeAdapter<LocalDateTime> { public static final class LocalDateTimeTypeAdapter
extends TemporalAccessorTypeAdapter<LocalDateTime, LocalDateTimeTypeAdapter> {
private final DateTimeFormatter dateTimeFormatter;
/** /**
* 默认构造函数, * 默认构造函数,
* 使用 {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} 进行 {@link LocalDateTime} 的序列化与反序列化。 * 使用 {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} 进行 {@link LocalDateTime} 的序列化与反序列化。
*/ */
public LocalDateTimeTypeAdapter() { public LocalDateTimeTypeAdapter() {
this.dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; this(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
} }
/** /**
@@ -101,20 +88,7 @@ public class JSR310TypeAdapters {
* @param formatter 用于序列化 {@link LocalDateTime} 的格式化器,不可为 {@code null}。 * @param formatter 用于序列化 {@link LocalDateTime} 的格式化器,不可为 {@code null}。
*/ */
public LocalDateTimeTypeAdapter(DateTimeFormatter formatter) { public LocalDateTimeTypeAdapter(DateTimeFormatter formatter) {
AssertTools.checkArgumentNotNull(formatter, "formatter can not be null."); super(LocalDateTime::from, formatter);
this.dateTimeFormatter = formatter;
}
/** {@inheritDoc} */
@Override
public void write(JsonWriter out, LocalDateTime value) throws IOException {
out.value(dateTimeFormatter.format(value));
}
/** {@inheritDoc} */
@Override
public LocalDateTime read(JsonReader in) throws IOException {
return LocalDateTime.parse(in.nextString(), dateTimeFormatter);
} }
} }
@@ -122,16 +96,15 @@ public class JSR310TypeAdapters {
* {@code ZonedDateTime} 的 {@code TypeAdapter} * {@code ZonedDateTime} 的 {@code TypeAdapter}
* 用于 Gson 对 {@code ZonedDateTime} 进行相互转换。 * 用于 Gson 对 {@code ZonedDateTime} 进行相互转换。
*/ */
public static final class ZonedDateTimeTypeAdapter extends TypeAdapter<ZonedDateTime> { public static final class ZonedDateTimeTypeAdapter
extends TemporalAccessorTypeAdapter<ZonedDateTime, ZonedDateTimeTypeAdapter> {
private final DateTimeFormatter dateTimeFormatter;
/** /**
* 默认构造函数, * 默认构造函数,
* 使用 {@link DateTimeFormatter#ISO_ZONED_DATE_TIME} 进行 {@link ZonedDateTime} 的序列化与反序列化。 * 使用 {@link DateTimeFormatter#ISO_ZONED_DATE_TIME} 进行 {@link ZonedDateTime} 的序列化与反序列化。
*/ */
public ZonedDateTimeTypeAdapter() { public ZonedDateTimeTypeAdapter() {
this.dateTimeFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; this(DateTimeFormatter.ISO_ZONED_DATE_TIME);
} }
/** /**
@@ -141,20 +114,7 @@ public class JSR310TypeAdapters {
* @param formatter 用于序列化 {@link ZonedDateTime} 的格式化器,不可为 {@code null}。 * @param formatter 用于序列化 {@link ZonedDateTime} 的格式化器,不可为 {@code null}。
*/ */
public ZonedDateTimeTypeAdapter(DateTimeFormatter formatter) { public ZonedDateTimeTypeAdapter(DateTimeFormatter formatter) {
AssertTools.checkArgumentNotNull(formatter, "formatter can not be null."); super(ZonedDateTime::from, formatter);
this.dateTimeFormatter = formatter;
}
/** {@inheritDoc} */
@Override
public void write(JsonWriter out, ZonedDateTime value) throws IOException {
out.value(dateTimeFormatter.format(value));
}
/** {@inheritDoc} */
@Override
public ZonedDateTime read(JsonReader in) throws IOException {
return ZonedDateTime.parse(in.nextString(), dateTimeFormatter);
} }
} }
@@ -166,18 +126,40 @@ public class JSR310TypeAdapters {
* 使用 {@link DateTimeFormatter#ISO_INSTANT} 进行 {@link Instant} 的序列化与反序列化。 * 使用 {@link DateTimeFormatter#ISO_INSTANT} 进行 {@link Instant} 的序列化与反序列化。
* *
*/ */
public static final class InstantTypeAdapter extends TypeAdapter<Instant> { public static final class InstantTypeAdapter
extends TemporalAccessorTypeAdapter<Instant, InstantTypeAdapter> {
/** {@inheritDoc} */ public InstantTypeAdapter() {
@Override super(Instant::from, DateTimeFormatter.ISO_INSTANT);
public void write(JsonWriter out, Instant value) throws IOException { }
out.value(DateTimeFormatter.ISO_INSTANT.format(value)); }
private abstract static class TemporalAccessorTypeAdapter<
T extends TemporalAccessor,
TTypeAdapter extends TemporalAccessorTypeAdapter<T, TTypeAdapter>>
extends TypeAdapter<T> {
private final TemporalQuery<T> temporalQuery;
private final DateTimeFormatter dateTimeFormatter;
protected TemporalAccessorTypeAdapter(
TemporalQuery<T> temporalQuery, DateTimeFormatter dateTimeFormatter) {
AssertTools.checkArgumentNotNull(dateTimeFormatter, "formatter must not be null.");
this.temporalQuery = temporalQuery;
this.dateTimeFormatter = dateTimeFormatter;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Instant read(JsonReader in) throws IOException { public void write(JsonWriter out, T value) throws IOException {
return Instant.parse(in.nextString()); out.value(dateTimeFormatter.format(value));
}
/** {@inheritDoc} */
@Override
public T read(JsonReader in) throws IOException {
return dateTimeFormatter.parse(in.nextString(), temporalQuery);
} }
} }

View File

@@ -19,14 +19,12 @@ package xyz.zhouxy.plusone.commons.model.dto.test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.Statement; import java.sql.Statement;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -45,9 +43,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -57,6 +52,9 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.gson.adapter.JSR310TypeAdapters.LocalDateTimeTypeAdapter;
import xyz.zhouxy.plusone.commons.gson.adapter.JSR310TypeAdapters.LocalDateTypeAdapter;
import xyz.zhouxy.plusone.commons.model.dto.PageResult; import xyz.zhouxy.plusone.commons.model.dto.PageResult;
import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams; import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams;
import xyz.zhouxy.plusone.commons.model.dto.PagingParams; import xyz.zhouxy.plusone.commons.model.dto.PagingParams;
@@ -187,31 +185,8 @@ public class PagingAndSortingQueryParamsTests {
@Test @Test
void testGson() { void testGson() {
Gson gson = new GsonBuilder() Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDate.class, new TypeAdapter<LocalDate>() { .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter().nullSafe())
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter().nullSafe())
@Override
public void write(JsonWriter out, LocalDate value) throws IOException {
out.value(DateTimeFormatter.ISO_DATE.format(value));
}
@Override
public LocalDate read(JsonReader in) throws IOException {
return LocalDate.parse(in.nextString(), DateTimeFormatter.ISO_DATE);
}
})
.registerTypeAdapter(LocalDateTime.class, new TypeAdapter<LocalDateTime>() {
@Override
public void write(JsonWriter out, LocalDateTime value) throws IOException {
out.value(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value));
}
@Override
public LocalDateTime read(JsonReader in) throws IOException {
return LocalDateTime.parse(in.nextString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
})
.create(); .create();
try (SqlSession session = sqlSessionFactory.openSession()) { try (SqlSession session = sqlSessionFactory.openSession()) {
AccountQueryParams params = gson.fromJson(JSON_STR, AccountQueryParams.class); AccountQueryParams params = gson.fromJson(JSON_STR, AccountQueryParams.class);