DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Data Engineering
  3. Data
  4. Data Software Design Pitfalls on Java: Should We Have a Constructor on JPA?

Data Software Design Pitfalls on Java: Should We Have a Constructor on JPA?

In this article, explore details on code, especially inside the Jakarta EE world, mainly to answer the questions: should we have a constructor on JPA, and why?

Otavio Santana user avatar by
Otavio Santana
CORE ·
Jul. 31, 22 · Opinion
Like (4)
Save
Tweet
Share
8.25K Views

Join the DZone community and get the full member experience.

Join For Free

The data in any modern and distributed architecture, such as microservices, work as a vein in a system. It fits like a state in a stateless application. On the other hand, we have the most popular paradigms in the code, especially when we talk about enterprise OOP. How do you combine both archive and software design, primarily on Java? 

This article will explore more details on code, especially inside the Jakarta EE world, mainly to answer the questions in a previous Jakarta JPA discussion: should we have a constructor on JPA, and why?

Context Data and Java

When we talk about Java and databases, the most systematic way to integrate both worlds is through thought frameworks. In the framework, we have types and categories based on communication levels and the usability of API.

  • Communication level: It defines how far the code is from a database or closer to the OOP domain. 
  • A driver is a framework level closer to OOP and domain, and far from a database. A driver we can smoothly work on is data-oriented. However, it might bring more boilerplate to obtain the code to the domain (e.g., JDBC).
  • A mapping goes in another direction, and thus, closer to OOP and far from the database. Where it reduces the boilerplate to a domain, we might face mismatch impedance and performance issues (e.g., Hibernate and Panache).
  • Usability of the API: Give an API, how many times will you use it for different databases? Once we have SQL as a standard on the relational database, we usually have one API for all database types.
  • A specific API is an API that works exclusively on a database. It often brings updates from this vendor; nonetheless, replacing a database means changing the whole API (e.g., Mophia, Neo4j-OGM Object Graph Mapper).
  • An agnostic API is a spread API where you have one API for many databases. It would be easier to use more databases, but the updates or particular database behavior are more challenging.

DDD vs. Data-Oriented

Whenever we talk about software design on Java, we mainly talk about the OOP paradigm. At the same time, a database is usually a different paradigm. The main difference is what we call the impedance mismatch. 

The OOP brings several approaches and good practices, such as encapsulation, composition, inheritance, polymorphism, etc., which won't have support on a database. 

You might read the book "Clean Code" where we have an Uncle Bob quote: "OOPs hide data to expose behavior." The DDD works this way to have a ubiquitous language and domain often around OOP. 

In his book "Data-Oriented Programming", author Yehonathan Sharvit proposes reducing complexity by promoting and treating data as a "first-class citizen."

This pattern summarizes three principles:

  1. The code is data separated.
  2. Data is immutable.
  3. Data has flexible access.

That is the biggest issue with both paradigms: it is hard to have both simultaneously, but it fits in the context. 

JPA and Data

The JPA is the most popular solution with relational databases. It is a Java standard to work, and we can see several platforms use it, such as Quarkus, Spring, and so on.

To fight against the impedance, JPA has several features to reduce this attraction, such as inheritance, where the JPA's implementation engine will translate to/from the database.

Java
 
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Product {
    @Id
    private long id;
    @Column
    private String name;

    //...
}


@Entity
public class Computer extends Product {

    @Column
    private String version;

}

@Entity
public class Food extends Product {

    @Column
    private Localdate expiry;

}


JPA and Constructor

Once we have the context, let's discuss this great Jakarta EE Ambassador discussion, and we also have a GitHub issue. 

We understand that there are always trade-offs when discussing software architecture and design. Thus, the enterprise architecture requires both DDD and a data-oriented approach based on the context.

Recently, Brian Goetz wrote an Oriented Data Programming in Java where he talks about how to archive success on data-programming using features such as record and sealed class.

It would be nice if we could explore and reuse record with JPA, but we have a legacy problem because JPA requires a default constructor. 

The question is, should it be enough? Or should JPA support more than OOP/DDD, ignoring the data programming? In my option, we should run for the data programming even if it breaks the previously-required default constructor.

"JPA requiring default constructors pretty much everywhere is a severe limitation to the entity design for dozens of reasons. Records make that pretty obvious. So, while you can argue that Persistence doesn't 'need ' to do anything regarding this aspect, I think it should. Because improving on this would broadly benefit Persistence, not only in persisting records." Oliver Drotbohm

We can imagine several scenarios where we can have benefits from the code design approach:

  • An immutable entity: We have a read-only entity. The source is the database. 
Java
 
public class City {
    
    private final String city;

    private final String country;

    public City(String city, String country) {
        this.city = city;
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public String getCountry() {
        return country;
    }
}


  • Force a bullet-proved entity: Imagine that we want both an immutable entity to force the consistency, and the entity is instantiated. So, we can combine it with Bean Validation to always create an entity when it brings valid values.
Java
 
public class Player {

    private final String name;

    private final  String city;

    private final  MonetaryAmount salary;

    private final  int score;

    private final  Position position;

    public Player(@Size(min = 5, max = 200) @NotBlank String name,
                  @Size(min = 5, max = 200) @NotBlank String city,
                  @NotNull MonetaryAmount salary,
                  @Min(0) int score,
                  @NotNull Position position) {
        this.name = name;
        this.city = city;
        this.salary = salary;
        this.score = score;
        this.position = position;
    }
}


JPA and Proposal

We learned from Agile methodology to release continuously and do a baby-step process. Consequently, we can start with support on two annotations, get feedback, fail-fast and then move it forward. 

As the first step, we can have a new annotation: constructor. Once we have it on the constructor, it will ignore the field annotations to use on the constructor. We can have support for two annotations: Id and Column.

Java
 
@Entity
public class Person {

    private final Long id;

    private final String name;

    @Constructor
    public Person(@Id Long id, @Column String name) {
        this.id = id;
        this.name = name;
    }

//...
    
}


We also should have support on Bean Validation on this step.

Java
 
@Entity
public class Person {
    @Id
    private final Long id;
    @Column
    private final String name;

    @Constructor
    public Person(@NotNull @Id Long id, @NotBlank @Column String name) {
        this.id = id;
        this.name = name;
    }

//...
    
}


You can explore records this case as well.

Java
 
@Entity
public record Person(@Id @NotNull Long id,  @NotBlank @Column String name){}


Annotations on a record component of a record class may be propagated to members and constructors of the record class as specified in 8.10.3.

The baby step is proposed and done. The next step is to receive feedback and points from the community.

Conclusion

The software design, mainly on OOP, is a rich world and brings several new perspectives. It is customary to review old concepts to get new ones. It happened with CDI, where it has improved the constructor to express a better design, and it should happen to JPA with the same proposal.con

Database Relational database Software design Data (computing) Java (programming language) Data Types

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • ChatGPT Prompts for Agile Practitioners
  • Top 10 Secure Coding Practices Every Developer Should Know
  • Load Balancing Pattern
  • 5 Factors When Selecting a Database

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: