Hi,
SpringPhysicalNamingStrategy
(applied by defaut by spring boot) does not work in this case.
@Entity
public class Book {
@Id
private int id;
@ElementCollection
private List<Comment> comments;
}
@Entity
public class User {
@Id
private int id;
@ElementCollection
@CollectionTable(name="book_comments")
private List<Comment> comments;
}
@Embeddable
public class Comment {
@ManyToOne
private User user;
@ManyToOne
private Book book;
private String text;
private Instant date;
}
error (when EntityManagerFactory
is created) : org.hibernate.MappingException: Repeated column in mapping for collection: Book.comments column: book_id
@Parent
cannot be used, because user
is not a parent if comments are accessed from the Book
, and book
is not a parent if comments are accessed from the User
.
Removing relations in Comment
solve the problem, setting the naming strategies (both physical and implicit) to hibernate default also solve the problem :
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
I don't know if this mapping is officially supported by Hibernate, but if Hibernate default strategy works, it could also work withSpringPhysicalNamingStrategy
.
Comment From: wilkinsona
Unfortunately, I cannot reproduce the problem that you have described. A minimal application with the three entities above starts successfully. Perhaps there's something more that's required to trigger the problem? If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: ah1508
Sure, here is a zip file with a simple project. spring-boot-issue24045.zip
Comment From: wilkinsona
Thanks for the sample. The two different physical naming strategies produce the following identifiers:
Hibernate | Spring |
---|---|
DTYPE | dtype |
Book | book |
id | id |
comments | comments |
comments_KEY | comments_key |
title | title |
Member | member |
firstname | firstname |
lastname | lastname |
book_comments | book_comments |
Member_id | member_id |
book | book |
book_id | book_id |
date | date |
member | member |
member_id | member_id |
text | text |
Book_id | book_id |
The difference with Spring Boot's strategy is that the identifiers are all in lower-case. This is described in the reference documentation:
By default, Spring Boot configures the physical naming strategy with SpringPhysicalNamingStrategy. This implementation provides the same table structure as Hibernate 4: all dots are replaced by underscores and camel casing is replaced by underscores as well. By default, all table names are generated in lower case, but it is possible to override that flag if your schema requires it.
As your schema requires mixed-case identifiers, you can override the case sensitivity flag by declaring the following bean:
@Bean
PhysicalNamingStrategy physicalNamingStrategy() {
return new SpringPhysicalNamingStrategy() {
@Override
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return false;
}
};
}
I think it would be useful if the documentation showed how to "override that flag if your schema requires it". We could include the example @Bean
definition above, for example. We can use this issue to do that.
Comment From: ah1508
Thanks for the explanation,
Any chance to configure this boolean through application.properties (something like spring.jpa.hibernate.naming.physical-strategy.case-insensitive
) ?
Comment From: wilkinsona
Thanks for the suggestion. Unfortunately, I think that would be rather confusing as it would only work when using the default strategy. In other words, the following configuration would ignore the proposed property:
spring.jpa.hibernate.naming.physical-strategy.case-insensitive=false
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Comment From: ah1508
I don't think it is a problem if this case-insensitive
flag only applies do the default strategy. If a developer wants to use hibernate defaults
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
It is then obvious that SpringPhysicalNamingStrategy
does not need to be configured.
My concern is about the developer who wants to use spring boot defaults (SpringPhysicalNamingStrategy
) but with case-insensitive
set to false. The definition of a PhysicalNamingStrategy
@Bean
is is a bit like killing a fly with a hammer. spring.jpa.hibernate.naming.physical-strategy.case-insensitive
might not be a good name since this flag is specific to SpringPhysicalNamingStrategy
, but how about spring.jpa.hibernate.naming.spring-physical-strategy.case-insensitive
?
Comment From: wilkinsona
Thanks again for the suggestion but we don't want to add a property for this. It's not a particularly common need and, no matter what the properties are called, I don't think it'll be possible to avoid the confusion caused by one property disabling another.