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
Partner Zones AWS Cloud
by AWS Developer Relations
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
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. JSON-Based Serialized LOB Pattern

JSON-Based Serialized LOB Pattern

Optimization of SQL queries with the help of Serialized LOB pattern implemented on JPA and Hibernate custom types for JSON

Ruslan Appazov user avatar by
Ruslan Appazov
·
Dec. 27, 22 · Code Snippet
Like (1)
Save
Tweet
Share
2.67K Views

Join the DZone community and get the full member experience.

Join For Free

Serialized LOB (Large Object) pattern is explained in the Patterns of Enterprise Application Architecture book by Martin Fowler. The original paper describes an implementation of the pattern with an example of XML-based serialization. Although nowadays, JSON format is ubiquitous. So, adding a fresh flavor of “JSONization” to the pattern seems fully justifiable.

There was mentioned that a binary serialization might win in size and performance, though the main problem with BLOBs (Binary Large Objects) remains the versioning. The changes in classes’ structure may lead to data incompatibility, so it can be problematic to de-serialize your objects from the database. Also, it is hardly achievable to query data inside BLOB. On the other hand, CLOB (Character Large Object) type is human readable, which at least allows for investigating issues. In addition, modern RDBM systems provide instruments to manipulate JSON, like querying and modifying data inside JSON.

Before diving into implementation details, let’s review the objectives of the pattern:

  • Simplifying a database structure by decreasing the number of related tables. This helps to reduce the number of joins and works better with small and immutable data. In general, this might be considered database denormalization.
  • Grouping table columns that together represent some object, and this object is an entity’s attribute. In other words, nonprimitive types are serialized and stored in a single column instead of multiple ones.
  • Arrange an entity’s attributes by their importance or by the frequency of usage. For example, key attributes that identify entities and participate in search should be mapped onto their own columns – a column per attribute. Other attributes, which are used occasionally, might be combined into a helper object, which is serialized and presented in the database as a single column.

Query Simplification

Imagine a site that shows basic information about products. At the minimum, products the table would have the structure:products table definition

If the site supports multiple languages, a localization might end up with something like this nightmare:

products table definition with multiple locales

Or with the better solution that uses a separate localization table:

multi-table products localization

The first variant is difficult to extend, and retrieving data by a locale would be awkward enough to avoid this option. SQL request for the second variant may be like this:

SQL
 
SELECT
  p.id, pl.name, pl.description 
FROM
  products AS p
  JOIN product_localization AS pl ON (p.id=pl.product_id) 
  JOIN languages AS l ON (pl.langauge_id=l.id)
WHERE 
  p.id=12345 AND l.code='de'


We can select data for a required locale, though we must join three tables.

With Serialized LOB pattern, it is possible to use an original structure of products the table. What is changed is the meaning and content of text columns. Instead of storing a single value of an attribute for some locale, the columns contain JSON string that presents LocalizedString an object, which in Java class with Lombok annotations and convenient method getByLocale might be implemented as the following:

Java
 
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class LocalizedString implements Serializable {
    private static final long serialVersionUID = 1L;
    private String en;
    private String it;
    private String de;
    public String getByLocale(@NotNull Locale locale) {
        switch (locale.getLanguage()) {
            case "it":
                return it;
            case "de":
                return de;
            default:
                return en; // a fallback in English
        }
    }
}


An example of JSON presentation of name attribute would like {“en”:”Hand plow”,”it”:”Aratro a mano”,”de”:”Handpflug”}.

Both name and description JSON can be stored directly in corresponding columns. So, it is possible to select data just by-product id without extra joins. Though this requires serialization/deserialization to and from JSON strings. A bit later, we will see how it can be implemented.

Object-Oriented Strategy

Serialized LOB pattern may also be helpful when your entity classes have attributes of object types. For instance, if you need to add size columns into products table, this can be done like this:

products table with separate columns for dimensions

But logically, these four new attributes present dimensions of the product. So, with the OO (object-oriented) approach, the following looks less verbose:

products table with dimensions packed into a single column

Java
 
@Getter
@Setter
@EqualsAndHashCode
@ToString
public static class Dimensions implements Serializable {
    private static final long serialVersionUID = 1L;
    private LocalizedString unit;
    private String height;
    private String length;
    private String weight;
    private String width;
}


Where the column dimensions store the JSON presentation of Dimensions object: {"unit":{"en":"meter","fr":"mètre","it":"metro"},"length":"1","width":"2","height":"3"}.

The OO approach works perfectly with immutable objects of stable, non-modifiable classes. Another example of such an immutable class is Location which might be a part of Address class:

Java
 
@Getter
@Setter
@EqualsAndHashCode
@ToString
public static class Location implements Serializable {
    private static final long serialVersionUID = 1L;
    private String latitude;
    private String longitude;
}


JSON presentation of Location the object looks like this: {"latitude":"-15.95615673677379", "longitude":"-5.69888813195389"}.

Multiple Attribute Packing

This approach is similar to the previous one – the packing attributes of an entity into an object, which is serialized into JSON. However, the requirements of the immutability of serialized classes might be dropped. The main reason to use patterns here is convenience.

For instance, you have Store a class with dozens of attributes. Some of them are necessary for identifying, searching, or displaying, like code, name, description. Others might be used only in rare specific cases, for example, defaultStorage, isEligibleForHomeDelivery, timeZone, etc. As a result, the stores the table might have so many columns that make it inconvenient to operate with. Instead of working with multiple queries, it is suggested to split attributes into groups. Key attributes are mapped onto dedicated columns, whereas other attributes, which are not used often, will be packed into a helper class as shown below:

moving store details attributes into a separate object

Where the column store_details contains the JSON presentation of an object of StoreDetails class:

Java
 
@Getter
@Setter
@EqualsAndHashCode
  public static class StoreDetails implements Serializable {
    private static final long serialVersionUID = 1L;
    private String timeZone;
    private String timeZoneDescription;
    private Boolean isEligibleForHomeDelivery;
    private Boolean isExpessDeliverySupported;
    private Boolean isLockerSupported;
    private Boolean defaultLocationId;
    private Location location; // safe, as Location class is considered as final
    private Address address; // Unsafe! Possible duplications and deserialization issues.
}


Pay attention to the last two attributes. It is quite safe to use fields of quasi-final types, like Location, whereas the usage of mutable classes, like Address, is rather risky. Firstly, Serialized LOBs might contain copies of the same data, e.g., the same address shared by different stores. Secondly, if a serialized LOB contains mutable objects inside JSON, the changes in those object classes may break compatibility between versions. For example, if JSON contains a fully serialized store address, StoreDetails the object cannot be restored from the JSON string if some attribute is removed from Address class.

JSON Serialization

Now let’s see how the pattern can be implemented with JSON serialization.

An obvious and straightforward technique, especially if you do not use ORM, is to serialize and de-serialize with the help of Gson or Jackson libraries. With that, while communicating with the DAO layer, you have to convert objects into JSON strings or parse JSON back from the string to the object. This is a simple approach, though it requires an implementation of custom logic in DAO.

A more elegant way to work with JSON data can be implemented if you use JPA and Hibernate. Actually, you do not need to create any intermediate layers at all. Instead, with the help of an excellent project hibernate-types, all you ought to do is declare and annotate your entities correspondingly. All the heavy lifting for converting to/from JSON will be done by custom type definitions which are provided by the hibernate-types project.

Here are the updated definitions of entities mentioned earlier for Hibernate 5:

Java
 
@Entity
@TypeDef(
    name = "json", typeClass = JsonStringType.class
)
@Getter
@Setter
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private LocalizedString name;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private LocalizedString description;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private Dimensions dimensions;

    @Getter
    @Setter
    @EqualsAndHashCode
    @ToString
    public static class Dimensions implements Serializable {
        private static final long serialVersionUID = 1L;
        private LocalizedString unit;
        private String height;
        private String length;
        private String weight;
        private String width;
    }
}


In the fragment above, it is shown how to declare JsonStringType the type to use with Product entity. Another approach is to place this declaration into a package info file. This is explained in the hibernate-types project documentation.

After declaration, the type can be used in annotations of attributes that are to be serialized into JSON when an entity is saved into the database and which are restored from JSON strings into Java objects. The above LocalizedString is annotated as json type. Highly likely, this class is used by multiple entities which require localized attributes. So, LocalizedString is declared as an independent class. On the other hand, Dimensions is defined as an embedded class, as hard it will be used in more than one entity.

Finally, here is an extended example of the definition Store the entity that uses all types of Serialized LOB patterns:

Java
 
@Entity
@TypeDef(
    name = "json", typeClass = JsonStringType.class
)
@Getter
@Setter
public class Store implements Serializable {

    private static final long serialVersionUID = 1;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String code;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private LocalizedString name;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private LocalizedString description;

    @Type(type = "json")
    @Column(columnDefinition = "varchar(max)")
    private StoreDetails storeDetails;

    @Getter
    @Setter
    @EqualsAndHashCode
    public static class StoreDetails implements Serializable {
        private static final long serialVersionUID = 1L;
        private String timeZone;
        private String timeZoneDescription;
        private Boolean isEligibleForHomeDelivery;
        private Boolean isExpessDeliverySupported;
        private Boolean isLockerSupported;
        private Boolean defaultLocationId;
        private Location location; // Safe as Location class is final
        private Address address; // Unsafe! Possible duplications and deserialization issues.
    }
    
    @Getter
    @Setter
    @EqualsAndHashCode
    @ToString
    public static class Location implements Serializable {
        private static final long serialVersionUID = 1L;
        private String latitude;
        private String longitude;
    }
}


Again, beware of using modifiable or referenced types inside objects which are the subject to Serialized LOB.

Conclusion

Prudently used, Serialized LOB pattern suggests an elegant and solid OO solution. With an implementation based on Hibernate custom types, all JSON manipulations are hidden behind annotations. So, a developer may work with objects not thinking about the mapping between classes and database tables or columns and perhaps even not suspecting that some objects are persisted as strings.

Database Decentralized autonomous organization Desktop environment JSON Attribute (computing) Data (computing) Object (computer science) sql Strings Data Types

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 4 Best dApp Frameworks for First-Time Ethereum Developers
  • What Is JavaScript Slice? Practical Examples and Guide
  • Documentation 101: How to Properly Document Your Cloud Infrastructure Project
  • Reliability Is Slowing You Down

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: