🔥 Articles, eBooks, Jobs, Columnist, Forum, Podcasts, Courses 🎓

Don't waste time defining user-friendly error messages for foreign key constraint violations in Java applications | ecode10.com


Don't waste time defining user-friendly error messages for foreign key constraint violations in Java applications

I'm obsessed about automating things so that I can

I'm obsessed about automating things so that I can focus on what really matters, such as core business rules. One common task every CRUD application has is handling database constraint violations, such as foreign key constraints, and returning a user-friendly message to the frontend.

This is a very common issue: you try to delete a city but there are customers registered for it. This way, the foreign key constraint won't allow the exclusion, ensuring data consistency.

As the number of tables in your database grows, the number of foreign keys increases as well. Dealing with all these constraint violations is repetitive and may become a pain.

When something bothers me, I try to find some way to make my life easier and more productive. Therefore, what if the backend could automatically handle such violations and generate a clear message to the user?

How the solution works

Using Java, Spring and Hibernate that is quit easy. First of all, you have to explicitly define a name for your foreign key constraints, such as fk_source_table__target_table.

To define the foreign key name for the Customer class using Hibernate/JPA, you can write your class like that:

@Entity
public class Customer {
  @Id
  public Long id;

  @ManyToOne
  @JoinColumn(foreignKey = @ForeignKey(name = "fk_customer__city"))
  public City city;
}

Let's break down this foreign key name pattern: source_table is the table where you are creating the foreign key and the target_table is the table being referenced. For instance, consider the city and customer example. The customer table will have a city_id field. To ensure relational integrity, you must create a foreign key for the customer table (source) that references the city table (target), such as:

alter table customer add constraint fk_customer__city foreign key (city_id) references city(id);

When I try to delete a city that has registered customers, the following will happen:

I'll get a database error that contains the name of the violated constraint. From the error message, I can infer that the user tried to delete a city (target_table) that has references on other table (source_table). Finally, I can extract the source_table and target_table from the error to compose a user-friendly message such as:

You cannot delete the 'city' because there are 'customer' records registered for it.

The solution is pretty straightforward but it has some issues and may not work for all kinds of applications:

The table names may not be exactly what you want to show to users. For instance, an order_item table may be shown without the underscore or simply as "item" instead of "order item". The underscore is easy to replace by a space, but changing the name of the table requires other strategies (like a dictionary). If your application has internationalization, that approach won't help much. But at least you can auto generate the error messages for a default language (such as English) and manually define the messages for additional languages.

How to ensure that the constraint name pattern is followed by all team members

I know you would ask that and I have you covered. Do you think that advanced Java features such as Reflection are used just by library and framework creators? Think again ??

Using Reflection, I can find the @ForeignKey annotations on class fields and get their metadata, such as the name attribute. But firstly, I need to look for the existing classes on the project so that I can check such annotations. That is also easy, I can lookup for Java files inside a given directory and load the classes from those files also using Reflection. Then, I can use a regular expression to check if the @ForeignKey names follow the pattern I've shown.

Finally, I just need to put this code inside a test class. This way, if a developer doesn't follow the name pattern, the test and build will fail. You can check how I implemented this test here. The entire project (which defines a feature-driven architecture for Spring Boot applications) is available at https://github.com/manoelcampos/springboot-feature-driven-arch-sample

Let me know in the comments what approach do you follow. I'd love to hear your thoughts.





Related articles




Top