Search code examples
javahibernatemigrationhibernate-4.xhibernate-5.x

Migrate Hibernate 4.3 to 5.2 - missing table


I'm trying to migrate from Hibernate 4.3 to 5.2. The problem I end up having is with the following entity structure:

@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Post<T> {

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "post_tags",
            joinColumns = {@JoinColumn(name = "post")},
            inverseJoinColumns = {@JoinColumn(name = "tag")})
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<Tag> tags = new HashSet<>();

    // ...
}


@Entity
@Table(name = "x_post")
public class XPost extends Post<X> {
    // ...
}


@Entity
@Table(name = "y_post")
public class YPost extends Post<Y> {
    // ...
}

Before the migration this generated 4 different tables: x_post, y_post, x_post_tags, y_post_tags

Now when I run the hibernate validation I get

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [post_tags] at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:121) at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42) at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89) at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)...

So Hibernate expects to have table post_tags now but I can't get why.

I suspect the problem is related to the naming strategy - previously I was using the ImprovedNamingStrategy but after the migration I had to add custom hibernate.physical_naming_strategy that looks like that:

public class MyPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl implements
        Serializable {

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return Identifier.toIdentifier(addUnderscores(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return Identifier.toIdentifier(addUnderscores(name.getText()), name.isQuoted());
    }

    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder(name.replace('.', '_'));
        for (int i = 1; i < buf.length() - 1; i++) {
            if (
                    Character.isLowerCase(buf.charAt(i - 1)) &&
                            Character.isUpperCase(buf.charAt(i)) &&
                            Character.isLowerCase(buf.charAt(i + 1))
                    ) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }
}

Update [2018-01-25]:

If I remove the @JoinTable(name = "post_tags") from the tags field the tables are generated as expected: x_post, y_post, x_post_tags, y_post_tags. If I leave it as it is in the example above it only generates the post_tags table.

So removing @JoinTable seem to fix the issue in that case, but if I want to have the join table name independent of the field tags (ex. if I want it to be singular - *_post_tag) I still need to use the @JoinTable which brings me back to the initial problem.


Solution

  • The JPA spec does not specify that 2 tables should be created in this case.

    So, in my opinion, if you explicitly specify the table name, then why would Hibernate try to change that?

    So, the Hibernate 5 behavior is more intuitive. Now, when you don't specify the @JoinTable and Hibernate generates two tables instead of one, you have two choices:

    1. Either you remove the @ManyToMany from the @MappedSuperclass and declare it explicitly in entities X and Y.
    2. You leave the @ManyToMany from the @MappedSuperclass and provide a custom PhysicalNamingStrategyStandardImpl that tries to override the default toPhysicalTableName to take into consideration the values you provided in @JoinTable.