0

Think Before You Hibernate

 2 years ago
source link: https://hackernoon.com/think-before-you-hibernate-dqt32g2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Think Before You Hibernate

TL;DR
4
heart.pngheart.pngheart.pngheart.png
light.pnglight.pnglight.pnglight.png
boat.pngboat.pngboat.pngboat.png
money.pngmoney.pngmoney.pngmoney.png
Hibernate may look like tons of annotations that magically select everything from the database and save it. Many programmers in job interviews cannot tell exactly how their application works. The second-level cache is a problem too.

@wirtzlegAleksandr Kozhenkov

Technical Lead at GridGain

I often saw how people included hibernating into a project, not really thinking about whether they really needed it. And after some time, when the service grew, they began to wonder if this was a mistake.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Let’s try to think ahead of what the pros and cons of hibernate are in general so that next time we can determine whether we need to add this dependency in a new microservice. Perhaps it makes sense to get by with simple Spring Data JDBC, without all that JPA complexity?

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Bunch of non-obvious things

Hibernate may look like tons of annotations that magically select everything from the database and save it. All we need is to add @ManyToMany@OneToMany, and other annotations correctly.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

If you dig deeper, many programmers in job interviews cannot tell exactly how their application works. For example, in the code below, we do not explicitly call the 

save()
 method anywhere, but changes will be saved in the database.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
@Transactional
public void processSomething(long accId) {
    Account acc = accRepo.findById(accId).orElseThrow(RuntimeException::new);

    acc.setLastName("new name");
}

To me, this kind of code makes it dramatically harder to read. Code reviewers will spend a lot of time figuring out what’s going on. And the next time you have to fix a bug, it will take a lot of time.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Lazy fetching issues

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
private Company company;

Another common occurrence is the use of Lazy fetch in transactions. It seems tempting to add a lazy field to the entity and refer to it in a transaction to get the data.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Then one more field is added, and one more. We most likely don’t need all of the data except 1–2 fields of several objects. But as a result, the application sends 5–10 requests to the database, completely pulling out objects. Instead, it is possible to write one select requesting only the necessary data.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

If the access to the fields occurs outside the service (and the transaction), there are still such ridiculous constructions that say hello to code reviewers:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
@Transactional(readOnly = true)
public Account getAccountWithCompany(long id) {
    Account acc = accRepo.findById(id).orElseThrow(RuntimeException::new);

    acc.getCompany().getName();

    return acc;
}

It would seem Eager fetching or @EntityGraph could be used. But Eager affects other code, and @EntityGraph requires a separate request (if we are already writing it, do we need a hibernate?).

0 reactions
heart.png
light.png
money.png
thumbs-down.png

N+1 selects problem

Writing code that will do batch inserts with hibernate is not an easy task. Don’t forget to:

0 reactions
heart.png
light.png
money.png
thumbs-down.png

1. Add a property to application.yml

spring.jpa.properties.hibername.jdbc.batch_size: 50
0 reactions
heart.png
light.png
money.png
thumbs-down.png

2. Create a sequence increasing by the batch size

CREATE SEQUENCE account_id_seq START 1 INCREMENT BY 50;
0 reactions
heart.png
light.png
money.png
thumbs-down.png

3. Setup sequence generator to use several sequence values at once. Otherwise, the hibernate will refer to the sequence N times.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
@Entity
public class Account {
    @Id
    @GenericGenerator(
        name = "account_id_generator",
        strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
            @Parameter(name = "sequence_name", value = "account_id_seq"),
            @Parameter(name = "increment_size", value = "50"),
            @Parameter(name = "optimizer", value = "pooled-lo")
        })
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "account_id_generator")
    private Long id;
    
    ...

It often happens that people forget about the sequence generator, and this completely negates the entire optimization of the batch insert.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

What’s really handy with Hibernate is the cascading batch insert if everything is well configured. Since the hibernate first requests IDs, this allows setting foreign keys for cascade saving of related entities. You can do it without Hibernate by following the same scenario.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Don’t forget to use @BatchSize to batch select entities associated with @ManyToMany or @OneToMany annotations. Otherwise, N+1 requests will be executed.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

If you nevertheless decided to use Hibernate, I recommend using the QuickPerf library in your tests to be sure how many requests to the database are executed exactly.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
@Test
@ExpectSelect(2)
public void shouldSelectTwoTimes() {
    ...
}

2nd level cache

Finally, it is worth mentioning the second-level cache in Hibernate. If you use the standard Ehcache implementation, then when the application is scaled, different instances will store different data in the cache.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

As a result, the response of the service may depend on which instance the request arrived at.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

To avoid this, it’s probably best to start using Spring Cache right away. Then, when scaling, it will be enough to connect a distributed implementation (Redis, Hazelcast, Apache Ignite, etc.).

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Let me also remind you that in using the L2 cache of Hibernate, a connection to a database will still be acquired for each request.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
2021-07-26 20:20:44.479  INFO 55184 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    4125 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    0 nanoseconds spent preparing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    12333 nanoseconds spent performing 1 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

Conclusion

I’m not trying to say that you should never use Hibernate. It can be useful in certain scenarios. But I want to say that you should always think about it in the early stages of a project not to regret it in the future.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Also published here.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
4
heart.pngheart.pngheart.pngheart.png
light.pnglight.pnglight.pnglight.png
boat.pngboat.pngboat.pngboat.png
money.pngmoney.pngmoney.pngmoney.png
by Aleksandr Kozhenkov @wirtzleg. Technical Lead at GridGainRead my stories
Join Hacker Noon

Create your free account to unlock your custom reading experience.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK