This code is a Spring Boot application that demonstrates the use of JPA (Java Persistence API) with lazy collections and bidirectional relationships. Here’s a breakdown of its components:
Interest and Person are JPA entities that represent the database tables.
@Entity @Data @NoArgsConstructor @RequiredArgsConstructor
class Interest {
@Id @GeneratedValue
private Long id;
@NonNull
private String name;
@ManyToMany(mappedBy = "interests")
private List<Person> people = new ArrayList();
public void addPerson(Person person) {
this.people.add(person);
if (!person.getInterests().contains(this)) {
person.addInterest(this);
}
}
}
- @Entity: Marks this class as a JPA entity.
- @Id and @GeneratedValue: Specifies the primary key and its generation strategy.
- @NonNull: Ensures that the name field is not null.
- @ManyToMany(mappedBy = "interests"): Defines a many-to-many relationship with the Person entity. The mappedBy attribute indicates that Interest is the inverse side of the relationship.
- addPerson(Person person): Adds a Person to the Interest and ensures the Person also adds this Interest to their own list.
@Entity @Data @NoArgsConstructor @RequiredArgsConstructor
class Person {
@Id @GeneratedValue
private Long id;
@NonNull
private String name;
@ManyToMany
private List<Interest> interests = new ArrayList();
public void addInterest(Interest interest) {
this.interests.add(interest);
if (!interest.getPeople().contains(this)) {
interest.addPerson(this);
}
}
}
- @Entity: Marks this class as a JPA entity.
- @Id and @GeneratedValue: Specifies the primary key and its generation strategy.
- @NonNull: Ensures that the name field is not null.
- @ManyToMany: Defines a many-to-many relationship with the Interest entity.
- addInterest(Interest interest): Adds an Interest to the Person and ensures the Interest also adds this Person to its own list.
interface InterestRepository extends JpaRepository<Interest, Long> {}
interface PersonRepository extends JpaRepository<Person, Long> {}
InterestRepository and PersonRepository are JPA repositories that provide CRUD operations for the Interest and Person entities, respectively.
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- @SpringBootApplication: Marks this class as the entry point of the Spring Boot application.
- main(String[] args): Starts the Spring Boot application.
@Order(1)
@Component @Data @Transactional
class AppInit implements ApplicationRunner {
private final PersonRepository personRepository;
private final InterestRepository interestRepository;
public void run(ApplicationArguments aa) {
Interest v = new Interest("Volleyball");
Interest a = new Interest("Art galleries");
Person s = new Person("Sabrina");
Person j = new Person("Jim");
s.addInterest(v);
j.addInterest(v);
j.addInterest(a);
personRepository.save(s);
personRepository.save(j);
interestRepository.save(v);
interestRepository.save(a);
}
}
- @Order(1): Specifies the order in which this component is executed.
- @Component: Marks this class as a Spring component.
- @Data: Generates getters, setters, and other common methods.
- @Transactional: Ensures that the operations within run() are executed within a transaction.
- run(ApplicationArguments aa): Creates and saves Interest and Person instances. It also establishes the relationships between Person and Interest.
@Order(2)
@Component @Data @Transactional
class AppInit2 implements ApplicationRunner {
private final PersonRepository personRepository;
public void run(ApplicationArguments aa) {
List<Person> people = personRepository.findAll();
for (Person p : people) {
System.out.println(p.getName() + ":");
List<Interest> interests = p.getInterests();
for (Interest i : interests) {
System.out.println(" shares " + i.getName() + " with:");
for (Person match : i.getPeople()) {
if (match != p) {
System.out.println(" " + match.getName());
}
}
}
}
}
}
- @Order(2): Specifies the order in which this component is executed.
- @Component: Marks this class as a Spring component.
- @Data: Generates getters, setters, and other common methods.
- @Transactional: Ensures that the operations within run() are executed within a transaction.
- run(ApplicationArguments aa): Fetches all Person entities and prints each Person and their interests, along with other people who share those interests.
- The Interest and Person entities are interconnected via a many-to-many relationship.
- AppInit initializes and saves the sample data.
- AppInit2 retrieves and displays the data to show how interests are shared among people.
- @Transactional ensures that the database operations are executed within a transaction, which is crucial for consistency.
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database (In-memory) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
In the provided code:
- @NoArgsConstructor: Generates a no-argument constructor for both the Interest and Person classes. This is needed for JPA entities because JPA requires a default constructor to create instances of entities via reflection.
- @RequiredArgsConstructor: Generates a constructor that takes parameters for all fields marked as @NonNull. In this case, it creates constructors for Interest and Person classes that require the name field, which is marked as @NonNull.