Files
homepage/docs/en-gb/blogs/java-development-cheatsheet.md

211 lines
7.3 KiB
Markdown

---
title: Java Development Cheatsheet
tags:
- java
- tips
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Comparison for `BigDecimal`
In Java, comparing `BigDecimal` values requires care because `equals` and `compareTo` behave differently.
### `equals` vs `compareTo`
`BigDecimal#equals` checks both **value** and **scale**, while `BigDecimal#compareTo` only checks **value** (ignoring scale). This means:
```java
var a = new BigDecimal("100"); // scale = 0
var b = new BigDecimal("100.00"); // scale = 2
a.equals(b); // false — different scale
a.compareTo(b); // 0 — same mathematical value
var c = new BigDecimal("200");
a.compareTo(c); // -1 (negative) — a is less than c
c.compareTo(a); // 1 (positive) — c is greater than a
```
### Why this matters
The scale mismatch often appears when values come from different sources — e.g., parsing user input, reading from a database (`DECIMAL(10,2)` columns), or receiving JSON payloads. You might think two values are equal when `equals` says they aren't.
### Rule of thumb
- Use **`compareTo`** for numeric equality checks: `a.compareTo(b) == 0`
- Use **`equals`** only when you mean "identical representation" (same value and same scale)
- Use **`stripTrailingZeros()`** if you need to normalise scale before `equals`:
```java
a.stripTrailingZeros().equals(b.stripTrailingZeros()); // true
```
### Comparing with zero
Avoid `==` or `.equals(BigDecimal.ZERO)` to check for zero — prefer `compareTo`:
```java
if (value.compareTo(BigDecimal.ZERO) == 0) { ... }
```
## How to Retrieve Data from a BlockingQueue
- `take()` — retrieves and removes the head of the queue, waiting if necessary until an element becomes available.
- `poll()` — retrieves and removes the head of the queue, or returns `null` if the queue is empty.
- `poll(long timeout, TimeUnit unit)` — retrieves and removes the head of the queue, waiting up to the specified wait time if necessary for an element to become available. Returns `null` if the timeout expires.
- `peek()` — retrieves but does not remove the head of the queue. Returns `null` if the queue is empty.
## Spring Cloud Alibaba FAQs
### How to prevent Nacos from creating a `nacos` folder in the user's home directory?
Add the following two configuration properties to specify the Nacos storage path:
- `JM.LOG.PATH`
- `JM.SNAPSHOT.PATH`
### How to deal with Sentinel's scattered log files?
Add the configuration property `csp.sentinel.log.dir` to change Sentinel's log directory.
### How to add Configuration Properties in JetBrains IntelliJ IDEA?
In JetBrains IntelliJ IDEA, click **`Edit Configurations…`** in the run configuration dropdown at the top right.
Click the **`Modify options`** button on the page, then add the properties you need to reset in the **`Override configuration properties`** table that appears below.
## Spring Data JPA FAQs
### How to fix the "Serializing `PageImpl` instances as-is not supported" warning?
Spring Data JPA warns about unstable JSON serialization of `PageImpl`. To resolve this, enable VIA_DTO serialization mode on your application's main class:
```java
@EnableSpringDataWebSupport(pageSerializationMode =
EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
```
### Why does the first page return no results?
JPA pagination is **zero-indexed**. Page `0` is the first page. If your frontend sends `page=1`, you need to pass `page - 1` to Spring Data:
```java
Pageable pageable = PageRequest.of(requestPage - 1, pageSize);
```
### How to avoid the N+1 query problem?
The N+1 problem occurs when JPA executes one query for the parent entity, then N additional queries for each child association.
**Detection** — look for repetitive SQL queries in the logs, or configure `spring.jpa.properties.hibernate.generate_statistics=true` to spot it.
**Fixes:**
| Approach | When to use |
|--------------------------|---------------------------------------------------|
| `@EntityGraph` | Declarative, good for entity-specific fetch plans |
| `JOIN FETCH` in `@Query` | Fine-grained control per query |
| `@BatchSize` | Reduces N+1 to N/k+1 by batching |
```java
// Option 1: EntityGraph
@EntityGraph(attributePaths = {"roles", "permissions"})
Optional<User> findById(long id);
// Option 2: JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
Optional<User> findByIdWithRoles(@Param("id") long id);
```
### `findById` vs `getReferenceById` — which one to use?
- **`findById`** — hits the database immediately, returns the entity or `Optional.empty()`. Use this when you need the data.
- **`getReferenceById`** — returns a lazy proxy **without** hitting the database. Throws `EntityNotFoundException` only when you access a non-existent proxy's properties. Use this when you only need the ID to set a foreign key relationship.
```java
// Good: only need the user reference to set a FK
Post post = new Post();
post.setAuthor(userRepository.getReferenceById(userId));
```
### How to fix `LazyInitializationException`?
This happens when you access a lazily-loaded association outside the persistence context (e.g., in a controller or serializer after the transaction has closed).
**Solutions:**
1. **Use `JOIN FETCH` or `@EntityGraph`** to eagerly load needed associations.
2. **Use DTO projections** — return only the fields you need instead of whole entities:
```java
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
UserDto findUserDtoById(@Param("id") long id);
```
3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope.
### When should I use `@Transactional(readOnly = true)`?
Use `@Transactional(readOnly = true)` on **read-only** service methods for three benefits:
- Hibernate skips dirty checking (no snapshots, less memory).
- The JDBC driver may route to read replicas.
- It documents the intent clearly.
```java
@Service
public class UserService {
@Transactional(readOnly = true)
public UserDto getUser(long id) { ... }
@Transactional
public UserDto createUser(CreateUserRequest request) { ... }
}
```
### `save()` vs `saveAll()` — which is faster for batch inserts?
`saveAll()` uses a single transaction and can benefit from JDBC batching. Configure the batch size:
```yaml
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 20
order_inserts: true
order_updates: true
```
For large bulk inserts (thousands of rows), consider `JdbcTemplate` batch operations instead — Hibernate's entity management overhead is significant at that scale.
### How to use dynamic queries with `Specification`?
For complex search forms with optional filters, use `JpaSpecificationExecutor`:
```java
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
// Usage
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (status != null) {
predicates.add(cb.equal(root.get("status"), status));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
Page<User> page = userRepository.findAll(spec, pageable);
```