Taogen's Blog

Stay hungry stay foolish.

类型改变,如 Integer 改为 String 或 Integer 改为 Long。

if (xxx.equals(Object obj))

当成员变量的数据类型改变后,直接量与成员变量比较时,比较结果可能和之前的结果不同。如:

new Integer(1).equals(new Long(1)) // 返回 false
new Integer(1).equals("1") // 返回 false
new Long(1).equals(1) // 返回 false

发生错误的场景,如:

Integer type1 = 1;
if (type1.equals(type)) {
...
}

解决方法:与该成员变量的比较的值,数据类型要保持一致。

map.get(Object key)

若该变量作为 map 的 key 时,可能出现新的数据类型在 map 中找不到对应的 key。

HashMap put(key, value) 方法有指定数据类型的约束,而 get(Object key) 方法没有对应的数据类型约束。字段类型改变后,put 方法有错误提示,而 get 方法没有提示。在 map 对象中不同的数据类型的 key 是不同的 key,从而会出现改完数据类型后找不到对应的 key。

Map<Integer, String> map = new HashMap()

解决方法:put(key, value) 和 get(Object key) 的 key 的数据类型要保持一致。

方法的重载

若该变量作为方法的参数时,存在多个重载方法,改完类型后,可能调用的不是之前的方法。如存在重载方法 getById(Integer id) 和 getById(Serializable id)。

解决方法:检查该成员变量作为参数调用方法的地方,跳转的方法是不是之前的方法。

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