Preventing duplicate ForeignKey generation
by Milo van der Zee
Hello all,
During development of applications I'm used to set the schema creation
to 'update' (hbm2ddl.auto = update). This makes life a bit easier.
Issue with the newer version of Hibernate is that the name of the
generated keys changed and so all keys are regenerated. For the large
databases I use this takes hours and has to be done every time a fresh
copy from production is taken to the development environment.
I do use meaningful names for the indexes where possible. But when using
abstract classes used by the entities that is not possible because the
same fields from the abstract are used by many entity classes and so
would end up having the same index names.
I checked the code that decides if the index needs to be created and
found that it only checks the name of the index. Not what the index
actually does. This is why I changed that piece of code to be a bit
smarter. It is desinged for simple constraints from one column to
another column. Not for multi to multi column indexes and constraints.
I created a Jira issue for it but nobody notices it and there are no
comments or anything else. So now I try it here :)
Jira HHH-10934 (https://hibernate.atlassian.net/browse/HHH-10934)
Code fragment I put in SchemaMigratorImpl.java:
private ForeignKeyInformation findMatchingForeignKey(ForeignKey foreignKey, TableInformation tableInformation) {
if (foreignKey.getName() ==null) {
return null;
}
/*
* Find existing keys based on referencing column and referencedTable
*/
String referencingColumn = foreignKey.getColumn(0).getName();
String referencedTableName = foreignKey.getReferencedTable().getName();
Iterable<ForeignKeyInformation> existingForeignKeys = tableInformation.getForeignKeys();
for (ForeignKeyInformation existingKey : existingForeignKeys) {
Iterable<ColumnReferenceMapping> columnReferenceMappings = existingKey.getColumnReferenceMappings();
for (ColumnReferenceMapping mapping : columnReferenceMappings) {
String existingReferencingColumn = mapping.getReferencingColumnMetadata().getColumnIdentifier().getText();
String existingReferencedTableName = mapping.getReferencedColumnMetadata().getContainingTableInformation().getName().getTableName().getCanonicalName();
if (referencingColumn.equals(existingReferencingColumn) && referencedTableName.equals(existingReferencedTableName)) {
return existingKey;
}
}
}
// If not yet found check based on key name return tableInformation.getForeignKey(Identifier.toIdentifier(foreignKey.getName()));
}
Or if you prever the Java 8 way:
private ForeignKeyInformation findMatchingForeignKey(ForeignKey foreignKey, TableInformation tableInformation) {
log.debug("findMatchingForeignKey");
if (foreignKey.getName() ==null)return null;
/*
* Find existing keys based on referencing column and referencedTable
*/
String referencingColumn = foreignKey.getColumn(0).getName();
String referencedTableName = foreignKey.getReferencedTable().getName();
Predicate<ColumnReferenceMapping> mappingPredicate = m -> referencingColumn.equals(m.getReferencingColumnMetadata().getColumnIdentifier().getText())
&& referencedTableName.equals(m.getReferencedColumnMetadata().getContainingTableInformation().getName().getTableName().getCanonicalName());
for (ForeignKeyInformation existingKey : tableInformation.getForeignKeys()) {
boolean found = StreamSupport.stream(existingKey.getColumnReferenceMappings().spliterator(),false).anyMatch(mappingPredicate);
if (found)return existingKey;
}
// If not yet found check based on key name return tableInformation.getForeignKey(Identifier.toIdentifier(foreignKey.getName()));
}
The calling method does not use the returned value. It only checks if
the returned value is null or not. So this could also be cleaned by
changing the method to return a boolean and then remove the for loop in
java-8 and use flatmap. But first let us agree on the validity of the
idea to change this piece of code.
I hope anybody would like to have a look at it and if there is any
change that the idea (not this actual very quick/dirty implementation)
goes into the system I'll clean it up and do some actual tests for more
complex database structures. I did not even check the junit tests yet.
At the moment it is good enough for me but I think it could be something
more people would benefit from.
Thanks,
Milo van der Zee
7 years, 7 months
Javassist dependency conflict in the ORM modules for WildFly
by Sanne Grinovero
Hi all,
there's a conflict in the Javassist versions of the WildFly modules of
Hibernate ORM 5.2.4.Final, but I'm not sure how to proceed as I'm not
familiar with this functionality.
The module declares *both*:
- a dependency to the WildFly provided Javassist: <module
name="org.javassist"/>
- inclusion of Hibernate's own version of Javassist: <resource-root
path="javassist-3.20.0-GA.jar"/>
Having both is causing trouble as they are both visible to the
Hibernate classloader; however I couldn't score a quick win with the
testsuiste by disabling either (all tests using these modules in the
Hibernate Search testsuite fail).
I suspect the problem is that JipiJapa also depends on the <module
name="org.javassist"/>, while ORM obviously needs the more recent
version of it.
I'm not familiar with what JipiJapa's business around Javassist; could
we keep the two clearly separated?
Especially if we want to make Byte Buddy a viable alternative, I
suspect the solution is that JipiJapa should not depend on Javassist
directly, but invoke a generic instrumentation SPI on Hibernate; also
with JipiJapa not able to see Javassist at all, we'd be in control of
the one and only Javassist version visible to ORM: the one we compile
and test with.
Thanks,
Sanne
7 years, 8 months
HHH-11144
by Gail Badner
HHH-11144 involves an entity that has 2 one-to-many associations with the
same type of entity, and both associations have orphanRemoval = true.
Andrea created a PR with test case at [1]
I am not sure if this is a valid mapping. I I would like your thoughts on
this.
Here is an excerpt of the relevant bits:
@Entity(name = "Item")
public static class Item {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval
= true)
protected Set<ItemRelation> lowerItemRelations = new LinkedHashSet<>();
@OneToMany(mappedBy = "child", cascade = CascadeType.ALL, orphanRemoval =
true)
protected Set<ItemRelation> higherItemRelations = new LinkedHashSet<>();
}
@Entity(name = "ItemRelation")
public static class ItemRelation {
@ManyToOne(optional = false)
@JoinColumn(name = "PARENT_ID")
private Item parent;
@ManyToOne(optional = false)
@JoinColumn(name = "CHILD_ID")
private Item child;
}
HHH-11144 describes inconsistent behavior observed when
Item#lowerItemRelations and Item#higherItemRelations both contain the same
ItemRelation, then one of the collections is cleared.
If the non-cleared collection is uninitialized, then the ItemRelation is
orphan deleted.
If the non-cleared collection is initialized, then the orphan-deleted
ItemRelation is rescued when PERSIST_ON_FLUSH cascades to the non-cleared
collection elements. The next time the collections are loaded from the
database, both will still contain that same ItemRelation.
The spec says:
"Portable applications must otherwise not depend upon a specific order of
removal, and must not reassign an entity that has been orphaned to another
relationship or *otherwise attempt to persist it*"
Is Hibernate doing the right thing by rescuing an orphan-deleted entity?
In addition, this mapping allows a particular EntityRelation to be
associated with 2 different Item entities, which would mean that the same
ItemRelation would have 2 different owners with respect to orphan deletion..
The spec says:
"The orphanRemoval functionality is intended for entities that are privately
“owned” by their parent entity."
Does this mean that the mapping is invalid, since it would allow multiple
parent entities, or does it mean that the resulting behavior is undefined
(thus non-portable)?
Please let me know your thoughts.
Thanks,
Gail
[1] https://github.com/hibernate/hibernate-orm/pull/1607
7 years, 9 months
dynamic instantiation queries
by Steve Ebersole
Historically (well before JPA) HIbernate would handle dynamic instantiation
queries in cases where one of the arguments being an entity-reference by
passing just the entity's identifier rather than a complete reference to
the entity. To be clear, I am talking about a query like:
select new DTO( p ) from Person p
Hibernate implicitly treats this like:
select new DTO( p.id ) from Person p
and expects DTO to have a ctor taking the appropriate ID type.
JPA came along and also defines support for dynamic instantiation queries,
but does not specify one way or the other how this case should be handled.
I have been told other providers interpret this the opposite way. Makes
sense. I think it is time we at least allow that as an option. Or maybe a
nicer implementation that looks for both and picks the available one (if
that's not too much effort).
What do y'all think?
7 years, 9 months
6.0 - ResultTransformer
by Steve Ebersole
Another legacy concept I'd like to revisit as we move to 6.0 is the
Hibernate ResultTransformer. I'd argue that ResultTransformer is no longer
needed, especially in it's current form.
Specifically, ResultTransformer defines 2 distinct ways to transform the
results of a query:
1. `#transformTuple` - this method operates on each "row" of the result,
allowing the user to transform the Object[] into some other structure.
This is specifically the one I see no value in moving forward. Between
dynamic-instantiation, Tuple-handling, etc I think users have the needed
capabilities to transform the query result tuples.
2. `#transformList` - this one operates on the query result as a whole
(unless scroll/iterate are used). This method at least adds something that
cannot be done in another way. But I'd still personally question its
overall usefulness.
Does anyone have an argument for continuing to support either of these?
Personally, I propose just dropping the ResultTransformer support
altogether.
7 years, 10 months
ScrollableResults and 6.0
by Steve Ebersole
For 6.0 I'd like to also make ScrollableResults parameterized wrt the "row
type". E.g.
ScrollableResults<Person> sr = session.createQuery( ..., Person.class
).scroll();
However that changes the signature of its methods returning a "row"
(currently always defined as Object[]).
How would everyone prefer we handle this? Do I just change the signatures
of the existing methods? Or add new methods?
7 years, 11 months
6.0 - Session#createFilter
by Steve Ebersole
As I have not been hearing hardly any feedback on these 6.0 design
questions I have been trying to start, I'll be doing something different in
this and any additional emails.. I'll state what I propose to do and if
anyone has issue with it they can make a counter proposal. Otherwise I
plan on following through with my proposal.
I plan on removing Session#createFilter. There are numerous reasons why
which I can discuss if anyone is interested in exploring this.
Ultimately I think it makes sense to handle this via Java 8 streams[1]
although I am not sure that needs to happen in 6.0
[1] https://hibernate.atlassian.net/browse/HHH-10962
7 years, 11 months
CI Jenkins upgraded to version 2.38
by Davide D'Alto
Hi,
I hope you all had great holidays.
I've upgraded our Jenkin on CI to version 2.38.
As far as i can tell everything seems all right, but if you experience
some unusual problems,
please, let me know.
Cheers,
Davide
7 years, 11 months
6.0 - Query literal rendering
by Steve Ebersole
Currently in 6.0 I have code in place to render query literals as
PreparedStatement parameters, but only outside the SELECT clause. Many DBs
require special handling for parameters defined in the SELECT clause
general requiring to wrap in cast functions calls.
I think it may be beneficial to allow the user to control this via a
setting. Specifically a multi-valued (enum) value with the following
possibility set:
1. NEVER
2. ALWAYS
3. OUTSIDE_SELECT
First, does anyone have an issue with this proposal? Secondly does anyone
see other concerns that should be take into account?
7 years, 11 months
AttributeConverter - 6.0
by Steve Ebersole
When I first added support for AttributeConverter I incorporated it into
BasicType because that was really the only option at the time.
As we move into 6.0 though I would like to consider moving this to the new
Attribute model. What this means practically speaking is that we'd
move AttributeConverter from the definition of BasicType and instead move
it to select "domain model reference nodes" identified by a
new ConvertibleDomainReference interface. This is already developed
initially in the 6.0 wip where basic singular attributes, basic collection
indexes and basic collection elements are all considered
ConvertibleDomainReferences.
This work so far only makes the AttributeConverters available
via ConvertibleDomainReference; it does not apply AttributeConverters
via ConvertibleDomainReference. That would be the next step if we decide
to go this route.
Additionally, this would mean that BasicType would no longer contain the
AttributeConverter; BasicType then would be the composition of:
1. JavaTypeDescriptor
2. SqlTypeDescriptor
3. MutabilityPlan
But even MutabilityPlan... I wonder if that should move to the Attribute
model as well?
Anyway, please let me know what y'all think of these open questions asap as
I'd like to start nailing down the Type SPIs for an Alpha.
7 years, 11 months