Taogen's Blog

Stay hungry stay foolish.

JSON Serialization

Custom Serializing Enumeration

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.taogen.demo.modules.task.enumeration.NameValueEnum;

import java.io.IOException;

/**
* Serializing Enums to JSON
*/
public class NameValueEnumSerializer extends StdSerializer<NameValueEnum> {

protected NameValueEnumSerializer() {
super(NameValueEnum.class);
}

protected NameValueEnumSerializer(Class t) {
super(t);
}

@Override
public void serialize(NameValueEnum nameValueEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("name");
jsonGenerator.writeString(nameValueEnum.getName());
jsonGenerator.writeFieldName("value");
jsonGenerator.writeString(nameValueEnum.getValue());
jsonGenerator.writeEndObject();
}
}
public interface NameValueEnum {

public String getName();

public void setName(String name);

public String getValue();

public void setValue(String value);
}
@JsonSerialize(using = NameValueEnumSerializer.class)
public enum TaskPriority implements NameValueEnum {
EMERGENCY("emergency", "紧急"),
HIGH("high", "高"),
GENERAL("general", "普通"),
LOW("low", "低")
;

private String name;

private String value;

@Override
@JsonValue
public String getName() {
return name;
}

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

@Override
public String getValue() {
return value;
}

@Override
public void setValue(String value) {
this.value = value;
}
}
public class Task extends BaseEntity {
private TaskPriority priority;
}

JSON Deserialization

Deserializing Comma Separated String to List

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 java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CommaSeparatedStrToListJsonDeserializer
extends JsonDeserializer<List<String>> {

@Override
public List<String> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
if ("{specifiedField}".equals(jsonParser.currentName())) {
String fieldStr = jsonParser.getValueAsString();
if (fieldStr != null && !fieldStr.trim().isEmpty()) {
return Arrays.stream(fieldStr.split(",")).collect(Collectors.toList());
}
}
return null;
}
}
@JsonDeserialize(using = CommaSeparatedStrToListJsonDeserializer.class)
private List<String> {specifiedField};

Deserializing JSON Array to String

public class JsonArrayToStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
if (jsonParser.currentToken() == JsonToken.START_ARRAY) {
List<String> list = new ArrayList<>();
jsonParser.nextToken();

while (jsonParser.hasCurrentToken() && jsonParser.currentToken() != JsonToken.END_ARRAY) {
list.add(jsonParser.getValueAsString());
jsonParser.nextToken();
}
return String.join(",", list);
}
return null;
}
}
@JsonDeserialize(using = JsonArrayToStringDeserializer.class)
private String {specifiedField};

References

[1] How To Serialize and Deserialize Enums with Jackson

Common Use

Character Encoding

useUnicode=true
characterEncoding=utf8

Data Processing

zeroDateTimeBehavior=convertToNull
tinyInt1isBit=false

Time Zone

serverTimezone=GMT%2B8

Connection

useSSL

For 8.0.12 and earlier: Use SSL when communicating with the server (true/false), default is ‘true’ when connecting to MySQL 5.5.45+, 5.6.26+ or 5.7.6+, otherwise default is ‘false’.

For 8.0.13 and later: Default is ‘true’. DEPRECATED. See sslMode property description for details.

  • Default Value: true
  • Since Version: 3.0.2

autoReconnect

Should the driver try to re-establish stale and/or dead connections

  • Default Value: false
  • Since Version: 1.1

maxReconnects

Maximum number of reconnects to attempt if autoReconnect is true, default is ‘3’.

useSSL=true
autoReconnect=true

Others

Timeout

initialTimeout

If autoReconnect is enabled, the initial time to wait between re-connect attempts (in seconds, defaults to ‘2’).

  • Default Value: 2
  • Since Version: 1.1

connectTimeout

Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. Defaults to ‘0’.

  • Default Value: 0
  • Since Version: 3.0.1

socketTimeout

Timeout (in milliseconds) on network socket operations (0, the default means no timeout).

  • Default Value: 0
  • Since Version: 3.0.1

References

[1] MySQL Connector/J 8.0 Configuration Properties

Background

Using MyBatis to query DATETIME type column data from MySQL table, if the column value is 0000-00-00 00:00:00, the program will throw exception Java.sql.SQLException. The following are the column properties.

pubtime DATETIME NULL DEFAULT NULL

Error Info

Error attempting to get column 'pubtime' from result set.  Cause: java.sql.SQLException: Zero date value prohibited
; Zero date value prohibited; nested exception is java.sql.SQLException: Zero date value prohibited
org.springframework.dao.TransientDataAccessResourceException: Error attempting to get column 'pubtime' from result set. Cause: java.sql.SQLException: Zero date value prohibited
; Zero date value prohibited; nested exception is java.sql.SQLException: Zero date value prohibited

Solutions

To set up zeroDateTimeBehavior=convertToNull in JdbcUrl. zeroDateTimeBehavior values can be EXCEPTION, ROUND, and CONVERT_TO_NULL. The default value of zeroDateTimeBehavior is EXCEPTION.

  1. Zero date will be converted to null
driver-url=jdbc:mysql://127.0.0.1/test?zeroDateTimeBehavior=convertToNull
  1. Zero date will be converted to 0001-01-01 00:00:00.0, equivalent to one year
driver-url=jdbc:mysql://127.0.0.1/test?zeroDateTimeBehavior=round

Reasons

When MySQL database is in the face of 0000-00-00 00:00:00 date processing, if not set corresponding countermeasures, it will produce an exception.

References

Download

Downloading the Activiti UI WAR file from the Activiti website.

Unzip activiti-6.0.0.zip.

Copy the activiti-6.0.0/wars/activiti-app.war to the webapps directory of Tomcat.

Configuration

Updating configurations in activiti-app/WEB-INF/classes/META-INF/activiti-app/activiti-app.properties

datasource.driver=com.mysql.cj.jdbc.Driver
datasource.url=jdbc:mysql://127.0.0.1:3306/activiti6ui?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=GMT
datasource.username={your_username}
datasource.password={your_password}

hibernate.dialect=org.hibernate.dialect.MySQLDialect

Adding MySQL 8 driver mysql-connector-java-8.0.19.jar to lib directory activiti-app/WEB-INF/lib.

Initializing Database

Creating database activiti6ui that configuring in activiti-app/WEB-INF/classes/META-INF/activiti-app/activiti-app.properties.

Executing following Activiti initial SQL files:

  • activiti-6.0.0/database/create/activiti.mysql.create.engine.sql
  • activiti-6.0.0/database/create/activiti.mysql.create.history.sql
  • activiti-6.0.0/database/create/activiti.mysql.create.identity.sql

Running and Visiting

Start Tomcat by running the startup.bat or startup.sh scripts in the bin folder of Tomcat.

When Tomcat is started open your browser and go to http://localhost:8080/activiti-app. Login with admin and password test.

The Solution

  1. Using @Component inject the bean to the Spring IoC container.
  2. Using @Value read application configurations of Spring into non-static fields.
  3. Implements InitializingBean override afterPropertiesSet() method. Assigning non-static field values to static field values in afterPropertiesSet() method.

For example:

@Component
public class FileUploadUtils implements InitializingBean
{
    private static String endpoint;
    private static String accessKeyId;
    private static String accessKeySecret;
    private static String bucketName;
    private static OSSClient ossClient;

    @Value("${app.fileUpload.oss.endpoint}")
    private String getEndpoint;

    @Value("${app.fileUpload.oss.accessKeyId}")
    private String getAccessKeyId;

    @Value("${app.fileUpload.oss.accessKeySecret}")
    private String getAccessKeySecret;

    @Value("${app.fileUpload.oss.bucketName}")
    private String getBucketName;

    @Override
    public void afterPropertiesSet() throws Exception {
        endpoint = getEndpoint;
        accessKeyId = getAccessKeyId;
        accessKeySecret = getAccessKeySecret;
        bucketName = getBucketName;
        ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
    }
}

Background

Intellij IDEA tips inspection error when injecting MyBatis mapper Java interface using @Autowired

@Autowired
private MyMapper mapper;

Error Info

Could not autowire. No beans of 'MyMapper' type found.

Solutions

It is not really an error, but error tips are annoying.

Solution 1: Add @Repository or @Component annotation in top of MyBatis Mapper interface classes.

@Repository
public interface MyMapper {}

Solution 2: Using @Resource annotation to inject MyBatis Mapper interface classes.

@Resouce
private MyMapper mapper;

References

[1] Idea inspects batis mapper bean wrong

Background

Compare date in where clauses of MyBatis mapper XML query.

<if test="beginTime != null and beginTime != ''">
createTime >= #{beginTime} and
</if>

Error Info

nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
### Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
### Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String

Solutions

remove beginTime != '' in the <if> element of MyBatis mapper XML file.

<if test="beginTime != null">
createTime >= #{beginTime} and
</if>

Reasons

When you pass Date objects as a parameter to MyBatis mapper XML query, you can’t compare the Date object with a String object in MyBatis <if> elements.

Java SE

@Deprecated // programmers are discouraged from using, typically because it is dangerous, or because a better alternative exists.
@FunctionalInterface
@Override // Indicates that a method declaration is intended to override a method declaration in a supertype.
@SuppressWarnings // Indicates that the named compiler warnings should be suppressed in the annotated element (and in all program elements contained in the annotated element).
@SafeVarargs // A programmer assertion that the body of the annotated method or constructor does not perform potentially unsafe operations on its varargs parameter.

Spring Projects

Spring

Bean declaration

@Component
@Controller
@Service
@Repository

Make a bean belong to a particular profile

@Profile("dev")
@Profile({"dev","test"})
// Only can contains one !string. Can't be like {"!dev","!test"}
@Profile("!dev")

Bean injection

@Autowired
@Resource

Specify injected bean name

@Qualifier("specificBeanName")

Bean initMethod and destroyMethod

@PostConstruct
public void init() {}
@PreDestroy
public void destroy() {}

Configurations

@Configuration
@Bean
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnWebApplication
@ConditionalExpression
@Conditional

Configuration file properties

@PropertySource

@PropertySource("classpath:my.properties")
public class MyProperties {}

@ConfigurationProperties

@ConfigurationProperties(prefix = "config.path")
@Configuration
@EnableConfigurationProperties
@Data
public class MyProperties {}
@Value("${databaseName}")
private String databaseName;

Spring MVC

Request

@Controller
@RestController
@RequestMapping
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

Request Information

@PathVariable
@RequestParam
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date datetime
@RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime time
@ModelAttribute
@RequestBody
@RequestHeader

Response

@ResponseBody
@ResponseHeader
@ResponseStatus

Convert Request Parameter Types

/**
* This method used in your Controller,
* or you can put the method in your BaseController.
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Convert field type from string to Date
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(YourEnum.class, new YourEnumConverter());
}

Data Validation

@Valid
@Validated(groupClass)

Data Validation Constraints Annotations

See JavaEE Validation Constraints Annotations

Exception Handling

@ExceptionHandler

Spring Data Access

@Transactional(rollbackFor=Exception.class)

By default all RuntimeExceptions rollback transaction whereas checked exceptions don’t. This is an EJB legacy. You can configure this by using rollbackFor() and noRollbackFor() annotation parameters: @Transactional(rollbackFor=Exception.class). This will rollback transaction after throwing any exception.

Programatically and manually roll back

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

Spring Security

@EnableWebSecurity

Spring Boot

Spring boot basic configuration

@SpringBootApplication
  • @SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration
  • @Configuration: This annotation marks a class as a Configuration class for Java-based configuration. This is particularly important if you favor Java-based configuration over XML configuration.
  • @ComponentScan: This annotation enables component-scanning so that the web controller classes and other components you create will be automatically discovered and registered as beans in Spring’s Application Context.
  • @EnableAutoConfiguration: This annotation enables the magical auto-configuration feature of Spring Boot, which can automatically configure a lot of stuff for you.

Scan MyBatis mappers

@MapperScan

For example

@MapperScan({"com.demo.dao", "com.demo.modules.**.dao"})

Wildcard

  • *: wildcard for one directory
  • **: wildcard for multiple level directory path

Java EE

Validation Constraints

validate object or string

@NotNull // a constrained CharSequence, Collection, Map, or Array is valid as long as it's not null, but it can be empty
@NotEmpty // a constrained CharSequence, Collection, Map, or Array is valid as long as it's not null and its size/length is greater than zero
@NotBlank // a constrained String is valid as long as it's not null and the trimmed length is greater than zero.
@Null
@Size // The annotated element size must be between the specified boundaries (included).
@Pattern // The annotated CharSequence must match the specified regular expression.
@Email

validate number

@Digits
@Min
@Max
@DecimalMax
@DecimalMin
@Positive
@PositiveOrZero
@Negative
@NegativeOrZero

validate datetime

@Future // The annotated element must be an instant, date or time in the future.
@FutureOrPresent
@Past // The annotated element must be an instant, date or time in the past.
@PastOrPresent

For more details refer to Java EE 8 javax.validation.constraints annotations

Jackson

JSON Serialization

Update format of serialized JSON value from Date type

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;

Update field name of serialized JSON

@JsonProperty(value = "user_password")
private String userPassword;

Ignore properties for JSON serialization

@JsonIgnore // for field
@JsonIgnoreProperties({"fieldname1", "fieldname2"}) // for POJO class
@JsonInclude // annotation used to define if certain "non-values" (nulls or empty values) should not be included when serializing

Serialize Enum to JSON String

@JsonSerialize(using = NameValueEnumSerializer.class)
public enum Type {
}

Property documentation, metadata

@JsonPropertyDescription

JSON Deserialization

Ignore Unknown Fields

@JsonIgnoreProperties(ignoreUnknown = true)

Convert JSON String to Date

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@DateTimeFormat(pattern = "HH:mm:ss")
private LocalTime alertTime;

Convert JSON String to Enum field (Note: The @JsonValue only deserialize for @RequestBody fields)

@JsonValue
public String getName() {
return name;
}

For more details refer to Jackson Annotations

Hibernate

@Entity
@Table
@Id
@GeneratedValue

MyBatis-Plus

Specify Table name

@TableName("t_user")

Specify primary key

@TableId(value = "id", type = IdType.AUTO)

Specify field is not as a column of table of database

@TableField(exist = false)

When field name same with keywords of database need use backquote to avoid SQL execution error

@TableField("`column`")
private String column;

Logic delete

@TableLogic(value = "0", delval = "1")
private Integer deleteFlag;

For more details refer to MyBatis-Plus Annotations

Lombok

POJO

@Data
@Getter/@Setter
@ToString
@ToString(callSuper = true)
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
@EqualsAndHashCode
@Builder // User.builder().name("Jack").build()
  • @Data: A shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor.

Generate a null-check statement

public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}

Result code

public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}

Automatic resource management: Call your close() methods safely with no hassle.

public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}

To sneakily throw checked exceptions without actually declaring this in your method’s throws clause

@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}

Result code

public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}

Annotate any class with a log annotation to let lombok generate a logger field.

@Log
@Log4j
@Log4j2
@Slf4j
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

Others

@Value
@Synchronized
@With
@Getter(lazy=true)
@RequiredArgsConstructor(onConstructor_ = @Autowired)

For more details refer to Lombok features.

You can rolling Lombok Back with Delombok tool.

JavaScript Frameworks

jQuery

jsDelivr

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>

React.js

unpkg

<!-- dev -->
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- prod -->
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

Vue.js

jsDelivr

<!-- dev -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- prod -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>

unpkg

<script src="https://unpkg.com/vue/dist/vue.js"></script>

UI Frameworks

jQuery-Based UI Frameworks

Bootstrap

jsDelivr

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>

LayUI

<link rel="stylesheet" type="text/css" href="https://www.layuicdn.com/layui/css/layui.css" />
<script src="https://www.layuicdn.com/layui/layui.js"></script>

VueJS-based UI Frameworks

Element UI

<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

React-based UI Frameworks

Material UI

unpkg

<!-- dev -->
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script>
<!-- prod -->
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.production.min.js"></script>

UI Components

Data Visualization

Apache Echarts

jsDelivr

<script src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/dist/echarts.min.js"></script>

Form Components

Rich Text Editors

wangEditor

<script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/wangeditor@latest/dist/wangEditor.min.js"
></script>

Background

When I using MyBatis-Plus’ com.baomidou.mybatisplus.extension.plugins.pagination.Page object as one of parameters passing mapper XML to find my page data, there are some errors.

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

public interface CustomerMapper extends BaseMapper<Customer> {
List<Customer> findPage(@Param("page") Page page, @Param("entity") Customer customer);
}

Error Info

org.springframework.jdbc.BadSqlGrammarException: 
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 0,10' at line 58
### The error may exist in file [D:\jtg\Repositories\manage-console-app\target\classes\mapper\modules\sysmp\CrmCustomerMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select ... limit 0, 10 LIMIT ?,?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 0,10' at line 58
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 0,10' at line 58

Solutions

Using yourself defined Page object as the parameter, don’t use MyBatis-Plus Page object.

Reasons

When you using MyBatis-Plus Page object as parameter passing to mapper XML, The MyBatis-Plus will automate add the string LIMIT ?,? to end of the mapper XML SQL.

0%