Create a Data Marvel — Part 6: Developing More Entities

Jennifer Reif
Jan 16 · 7 min read

After last week’s Part 5, we have the beginnings of our domain classes. We developed the Character domain, along with its repository interface and a controller to manage request/response. This week, we will continue that momentum and create classes for another 4 areas of our graph data model — creator, event, series, and story.

We will see how our Character classes gave us a foundation that simplifies building the next set of classes. Let’s get started!

Organizing classes

Before we dive right into code, we should do a bit of housekeeping. As you might imagine, when we build out all 6 of our data model entities with 3 (or more) classes each, we will end up with quite a long list of classes in our demo folder. To help organize this, we can add subfolders for each entity to drop each set of classes.

Starting with our existing Character classes, we can right-mouse click on the demo folder (src->main->java->com->example->demo) and choose New, then Package.

Type in the name character, then click OK. Now we need to move our existing classes to the new folder. Select all three classes (Character, CharacterController, CharacterRepo), then drag and drop them on the new character folder. A popup box will appear to ensure you want to refactor all references and paths on the classes. Ensure the boxes are checked and the folder path looks correct, then click Refactor.

Once this is complete, you should now see a structure like in the image below.

Let’s go ahead and create the folders for each of the other classes. You can follow the same steps we executed above, but name each of the packages as follows: creator, comicissue, event, series, story. If you completed those steps, you should now see a structure like this.

Domain classes

The Character classes that we coded last time gave us the foundation for the annotations, structure, and some fields that we can use across each of our other entities. Because of this, the other non-central entities (creator, event, series, and story) are able to be coded very easily and quickly.

The Creator class

Now that we have our folders set up, we are ready to start coding our Creator classes. We need to right-mouse click on our new creator package and choose New->Java Class. Name it Creator and click OK. Again, IntelliJ generates the class path and the empty class declaration for us, so we can fill in the gaps.

Just like we did with the Character class, we add our Lombok annotations and our fields to this. The code for the class is below. We will review each component in the next paragraph.

package com.example.demo.creator;import ...@Data
@NoArgsConstructor
@RequiredArgsConstructor
@NodeEntity
public class Creator {
@Id @GeneratedValue
private Long neoId;
@NonNull
private Long id;
@NonNull
private String name, resourceURI;
}

The @Data annotation handles much of the boilerplate for the standard POJOs, supplying the getters, setters, and equals+hashcode+toString methods. The next Lombok annotations @NoArgsConstructor and @RequiredArgsConstructor generate a constructor with no arguments and a constructor with one argument for each field required to be @NonNull, respectively. We also need to annotate the class with @NodeEntity to let Spring Data Neo4j (SDN) know that this is a domain class for a node in our graph database.

Within our Creator class declaration, we set up additional annotations on each of our fields. The @Id and @GeneratedValue annotations show which field is the id (neoId) and that the value will not be user-defined. This field is the internal unique id that Neo4j creates for each node and relationship. Though it is rarely referenced, it is part of the entity in the database.

The next field (id) is the id coming from the Marvel system, so it’s the API id. We used a Lombok @NonNull annotation here to handle a null-check and ensure the value is not null. The final line has the String fields we felt most meaningful to this entity from the API. These fields also have the non-null annotation to ensure values are not missing.

Our Creator class is completed! It’s time to add our repository interface and our controller class to it.

The repository interface

We need the repository interface for the data access layer. To start, right-mouse click on the creator package folder again and choose New->Java Class. Name it CreatorRepo, then be sure to choose the Kind as Interface before clicking OK.

Since this class mirrors the CharacterRepo we coded last time, we will skip to the full code. All we need to do is extend the interface from the Neo4jRepository.

package com.example.demo.creator;import org.springframework.data.neo4j.repository.Neo4jRepository;public interface CreatorRepo extends Neo4jRepository<Creator, Long> {}

The controller class

To wrap up our Creator classes, we need to add the controller class that will handle requests and responses between the user interface and data layers. This class carries the bulk of the logic.

This class’s code is similar to ourCharacterController. A brief explanation follows the code.

package com.example.demo.creator;import ...@RestController
@RequestMapping(“/creators”)
public class CreatorController {
private final CreatorRepo repo;
public CreatorController(CreatorRepo repo) { this.repo = repo; } @GetMapping
public Iterable<Creator> getAllCreators() {
return repo.findAll();
}
}

Remember that the @RestController annotation eliminates the need to put @ResponseBody on each request handling method, and @RequestMapping maps requests to a controller method for a certain path (in this case, /creators).

Inside the class declaration, we have a local member variable and a constructor for our CreatorRepo to inject the repository into our controller. The @GetMapping annotation tells us this will be a GET method and precedes the method declaration for getAllCreators() that accesses the repository, executes the provided findAll() method, and returns multiple Creator entities (Iterable of type Creator).

Remaining classes

Following these steps above, we can push on through the rest of the classes easily. No new or different code is needed to build out the other entities. The code for each of these will be listed, followed by a final folder structure at the end for reference and verification. See you at the finish line! :)

Event:

package com.example.demo.event;import ...@Data
@NoArgsConstructor
@RequiredArgsConstructor
@NodeEntity
public class Event {
@Id @GeneratedValue
private Long neoId;
@NonNull
private Long id;
@NonNull
private String name, resourceURI;
}

EventRepo:

package com.example.demo.event;import org.springframework.data.neo4j.repository.Neo4jRepository;public interface EventRepo extends Neo4jRepository<Event, Long> {}

EventController:

package com.example.demo.event;import ...@RestController
@RequestMapping(“/events”)
public class EventController {
private final EventRepo repo;

public EventController(EventRepo repo) { this.repo = repo; }
@GetMapping
public Iterable<Event> getAllEvent() {
return repo.findAll();
}
}

Series:

package com.example.demo.series;import ...@Data
@NoArgsConstructor
@RequiredArgsConstructor
@NodeEntity
public class Series {
@Id @GeneratedValue
private Long neoId;
@NonNull
private Long id;
@NonNull
private String name, resourceURI, thumbnail;
@NonNull
private Integer startYear, endYear;
}

SeriesRepo:

package com.example.demo.series;import org.springframework.data.neo4j.repository.Neo4jRepository;public interface SeriesRepo extends Neo4jRepository<Series, Long> {}

SeriesController:

package com.example.demo.series;import ...@RestController
@RequestMapping(“/series”)
public class SeriesController {
private final SeriesRepo repo;
public SeriesController(SeriesRepo repo) { this.repo = repo; } @GetMapping
public Iterable<Series> getAllSeries() {
return repo.findAll();
}
}

Story:

package com.example.demo.story;

import ...

@Data
@NoArgsConstructor
@RequiredArgsConstructor
@NodeEntity
public class Story {
@Id @GeneratedValue
private Long neoId;
@NonNull
private Long id;
@NonNull
private String name, resourceURI, type;
}

StoryRepo:

package com.example.demo.story;import org.springframework.data.neo4j.repository.Neo4jRepository;public interface StoryRepo extends Neo4jRepository<Story, Long> {}

StoryController:

package com.example.demo.story;import @RestController
@RequestMapping(“/stories”)
public class StoryController {
private final StoryRepo repo;
public StoryController(StoryRepo repo) { this.repo = repo; } public Iterable<Story> getAllStories() {
return repo.findAll();
}
}

Wrapping up

Whew! We made it. That wraps up code for our domain classes for 5 out of 6 entities. That’s correct, we still have one more set of classes to cover for the ComicIssue. The comic issue node is the center for all of our relationship connections to the other nodes, so the code will require more depth and explanation.

What I Learned

Most of this code mirrored what we did in the last post with our Character classes. This made it really simple to code the Creator, Event, Series, and Story classes quickly and efficiently and reduce explanation.

Let’s look at the key takeaways here.

  1. We didn’t need any additional fluff in our code, and Spring keeps the overhead low, so we minimize repetitive code. In about 5min, we could whip up the rest of these classes and have them function. It makes it incredibly helpful in our short development process (and for later live-coding times!).
  2. None of our entities had any extraneous or unusual logic or code needed. We could have added more methods or entrypoints to access different entity aspects, but our goal was to keep it simple and clean. This made the code nearly copy across our 5 non-central entities.

Next Steps

We now have the bulk of our supporting code developed and are ready to work through the final piece that connects them all. Stay tuned for the next post where we will code the ComicIssue classes and show how to tie it all together!

Resources

Neo4j Developer Blog

Developer Content around Graph Databases, Neo4j, Cypher, Data Science, Graph Analytics, GraphQL and more.

Jennifer Reif

Written by

Jennifer Reif is an avid developer and problem-solver. She enjoys learning new technologies, sometimes on a daily basis! Her Twitter handle is @JMHReif.

Neo4j Developer Blog

Developer Content around Graph Databases, Neo4j, Cypher, Data Science, Graph Analytics, GraphQL and more.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade