1

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.

nyxz
  • 6,918
  • 9
  • 54
  • 67
  • You tell the join table to be named "post_tags". Why would you expect the table name to be different then? Hibernate evaluates the name of the join table and passes that to your naming strategy. – FarwoodHill Jan 23 '18 at 16:29
  • I expect to have the same tables generated as they were before the migration to Hibernate 5. I haven't added the JoinTable annotation now. It's left over from before the migration. – nyxz Jan 23 '18 at 16:40
  • This is precisely why reading migration documents are important. For every minor and major release of Hibernate, there will often be new configuration parameters or strategy changes to support better, often more efficient ways of doing something. In some cases you can set a config option to get the legacy behavior and in other cases you may have to supply a custom implementation as a part of the migration if you want your legacy application to continue to behave in the legacy way. – Naros Jan 23 '18 at 18:43
  • @Naros Yes, keeping the legacy behavior of my app is exactly what I want to achieve. I've read the migration page here https://github.com/hibernate/hibernate-orm/blob/5.0/migration-guide.adoc and I tried to fix my migration issues by setting the implicit strategy to "legacy-hbm" as suggested in the **Changed setting defaults** section but without success. If you have more concrete suggestion I would be happy to try it out. – nyxz Jan 23 '18 at 22:03

1 Answers1

2

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.
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911