[JAVA] SpringBoot-JPA-Hibernate addictive memo

About this article

This article summarizes what I was addicted to while developing the application, and if I could find it, the cause and solution. There may be cases where the problem is in this article, but the usage is wrong in the first place, or there is a better solution.

environment

Problems encountered

I get an error when I make an association with a key other than PK

As is usually the case with databases, there can be issues with the Hibernate implementation.

Use Case

@Entity
class Parent {
    @Id
    Long id;
    @NatulalId
    Integer key;
    String firstName;
    String lastName;
}

@Entity
class Child {
   @Id
   Long id;
   @ManyToOne
   @JoinColumn(name = "parent_key",referencedColumnName = "key")
   Parent parent;
}

In the above usage where the PK recommended by Hibernate itself is the surrogate key and the natural key is specified by @NaturalId, the key is specified as Unique within the range of the JPA specification, and instead of the id. You want it to function as a foreign key. If you associate with a natural key, an error will occur due to cast involvement when executing child.getParent () etc. [^ 1] [^ 1]: It doesn't seem to happen all the time, and it seems that questions about that are often posted.

Cause

Official issue tracker HHH-7668

Solution

Make the referenced entity (Parent in the above) Sirializable. With this in mind, it may be safer for all entities used in Hibernate to be Sirializable.

A quick look at the issue tracker shows that if you associate it with a unique column that is not PK, you may encounter other problems.

2020/05/28 postscript About the entity Sirializable

Due to JPA specifications, Sirializable is not required when an entity is exchanged only with DB, but Sirializable of an entity is required when it is used within the scope of JavaEE, such as saving it as an object in an HTTP session. It seems like. Considering that an error occurs in an unexpected place, it seems that it is desirable to be Sirializable.

FetchType.Lazy entity throws error when serialized by Jackson

Cause

Because the entity of the Lazy specified property that Jackson is accessing when serializing is a Hibernate proxy

Solution

Add the Jackson plug-in for the Hibernate version. Spring Boot depends on Hibernate and Jackson by default, so why isn't this included? [^ 2] [^ 2]: I noticed while writing the article, but it is included in Spring Data REST Web MVC ... Why? Stack Overflow: No serializer found for ...? Stack OverFlow: Spring Jackson suppresses Lazy attribute Everyone is having a hard time.

Solving the problem that Jackson serialization of Spring JPA entity also triggers Lazy The above is a very straightforward explanation and solution.

So just in case, it's Plugin GitHub. The content is not very kind. Incidentally, Explanation of output control by Jackson annotation (infinite loop workaround) introduced in the above GitHub.

An error related to column name or table name appears (addition / correction 2020-02-21)

This is a specification story. It's not a bug.

Cause

~~ The default naming strategy is processed after JPA annotations ~~

~~ If an annotation that specifies a JPA (‚Čí Hibernate) table name or column name is used simply because of the priority problem, an error will occur because the column name conversion from the field name has not been processed. ~~

I didn't understand the specifications enough. Hibernate will generate the DB column name through a two-step conversion.

  1. Generate a logical name from entity name and field name
  2. Convert logical names to physical names

The current Spring default is

  1. Generation of logical name --Get class name and field name as ** logical name ** --If specified by @ Column or @ Table, change the character string to ** logical name **

  2. Generation of physical name --Replace period with underscore --Replace camel case with snake case

It is set as.

Since the character string specified by @ Column or @ Table is changed to ** logical name **, if the field or class name is specified in camel case, the name specified in the annotation should not be changed to camel case. Must not be. If a physical name (that is, a snake case) is specified here, "Table [table name] contains physical column name [column name] referred to by multiple logical column names:" (logical corresponding to the physical column name of the table) I have two names) and I get an error.

Solution

If the column name etc. is specified in the ~~ JPA annotation, the name must be specified in the field with @Column. I can't cut corners more than I expected. ~~ [^ 3] [^ 3]: ~~ I think it can be solved by customizing on the Hibernate side. ~~ Read the specifications and error messages properly. Since the physical name is generated in all lowercase letters, it is better to use uppercase column names and table names.

apprication.yaml



spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

(The logical name is used as the physical name as it is). However, Spring ImplicitNamingStrategy, which Spring uses by default, uses Hibernate's default ImplicitNamingStrategyJpaCompliantImpl almost as it is, so if you use the property name of camel case, it will be the column name of camel case as it is. Refer to stackoverfrow: Hibernate naming strategy example and select the closest one and adjust it with annotations. think. If it's not enough to use Hibernate's strategy, you'll have to implement it yourself. reference: Adjusted with Physical Naming Strategy Adjusted with ImplicitNamingStrategyJpaCompliantImpl

I want to have the related entity and the actual value of the key in different properties

This is not a problem, it's a tip. It's a convenient way to create an entity that wants to keep the actual value of the key in a property and follow the association only when needed.

Solution

Disable update and insert of related entities with @ JoinColumn

@Entity
class Parent {
    @Id
    Long id;
    @NatulalId
    Integer key;
    String firstName;
    String lastName;
}

@Entity
class Child {
   @Id
   Long id;
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "parent_key",referencedColumnName = "key",
        insertable = false, updatable = false)
   Parent parent;
   @Column(name="parent_key")
   Integer parentKey;
}

By making it a class like the above, it is possible to refer to the parent while holding the key directly, and it is possible to prevent the child from updating the parent. When updating the reference, directly change the value of the key field (parentKey in this example). This shape works very well for objects that reference master data.

Recommended Posts

SpringBoot-JPA-Hibernate addictive memo
Integer memo
docker memo
Lombok memo
Dockerfile memo
Java memo
AWS memo
Dcokerfile memo
Ruby memo
Memo Stream