4 Database tips for Java Developers 💡
Published: 2024-07-08 00:18:44

### Introduction

🔍 Understanding databases is essential for Java developers, as they frequently handle critical data such as user information, past actions, application settings, and feature flags. As applications expand, databases must scale to meet performance demands and ensure efficient data management. For insights into scaling databases effectively, refer to my [article on optimizing bank data growth with sharding architecture.](https://dev.to/joaomarques/optimizing-bank-data-growth-with-sharding-architecture-3pnf) 

🚀 Drawing from my experience, I've wrote 4 essential tips to enhance your database practices in Java.

### 1. Always use an ORM Framework (Please do that, think about your future dev colleagues 😅)
💡 **Why:** ORM frameworks like Hibernate or JPA help manage database interactions efficiently, reducing boilerplate code and they manage the protection for SQL injections.
Trust me, big applications without orm are very hard to maintain and takes more time than you think to manage simple things.

💻 **Example:** Using Hibernate with JPA annotations.

```java

import javax.persistence.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username", nullable = false, unique = true)
    private String username;

    @Column(name = "email", nullable = false, unique = true)
    private String email;

    // Getters and Setters (I'd ratter use lombok)
}
```

**Repository:**

```java
// Repository class using Spring Data JPA
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}
```

### 2. Validate Input Data
💡 **Why:** Validating data ensures that only correct and expected data is stored, preventing corruption and errors.
Make sure your services are ready catch exceptions coming from the database.

💻 **Example:** Using Hibernate Validator.

```java

import javax.validation.constraints.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min = 5, max = 15)
    private String username;

    @NotNull
    @Email
    private String email;

    // Getters and Setters (I'd ratter use lombok)
}
```

### 3. Implement Database Transactions
💡 **Why:** Transactions ensure that you database transactions can always rollback in case an exception happens during thread execution.


💻 **Example:** Using Spring's `@Transactional` annotation.
In this example, in case `userActivityRepository.save(activity)` throws an exception `userRepository.save(user)` will be rolled back.

```java

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUserAndSaveActivity(User user, UserActivity activity) {
        userRepository.save(user);
        userActivityRepository.save(activity);
    }
}
```

### 4. Monitor Queries
💡 **Why:** Monitoring queries can inform your team where in your application is causing the bottleneck.
The team can create jira tickets to work on the performance for those bottleneck queries.

💻 **Example:** Using Hibernate's statistics with the `@Timed` annotation from the Spring Metrics module to monitor the performance.

```java

import org.springframework.stereotype.Repository;
import org.springframework.metrics.annotation.Timed;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

@Repository
public class UserRepository {

    // PersistenceContext was used only for example
    @PersistenceContext
    private EntityManager entityManager;

    @Timed
    public List<User> getUsersWithOptimizedQuery(String role) {
        return entityManager.createQuery("SELECT u FROM User u WHERE u.role.name = :roleName", User.class)
                            .setParameter("roleName", role)
                            .getResultList();
    }
}
```

📈 Implementing these strategies are just the basis to keep your database clean and efficient when working with Java applications, that can grow anytime.