I am trying to define xml mapping for a Map<String,String> field.
The entity class cannot be modified so I am using the XML variant of JPA mapping, but cannot figure out the proper syntax.
Can someone explain how to write the JPA xml for this case - or explicitly state that this is impossible with xml but possible with annotations as mentioned in Storing a Map<String,String> using JPA ...
I will even appreciate to know that this is impossible - ideally when it comes with reference to the part of specification that states it.
These primitive relations have been added in JPA2 so you have to use a JPA2 imeplementation. I use Eclipselink. The keyword is "ElementCollection". It seems this has allready been discussed here:
Storing a Map<String,String> using JPA
After more time and searching for different things I happened to find the answer here:
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/new_collection_mappings#XML_2
The solution is:
<element-collection name="quotes">
<column name="QUOTE"/>
<map-key-column name="Q_DATE"/>
<collection-table name="EBC_QUOTES">
<join-column name="EBC_ID"/>
</collection-table>
</element-collection>
You haven't specified which JPA implementation you're using, but I think this should work for both OpenJPA and Hibernate... See here:
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Example_of_a_map_key_column_relationship_XML
The difficulty you'll run into is that you're mapping to a primitive type instead of an entity type. I won't say it's impossible, but I will say from experience that it's painful.
Related
i'm trying to map some classes using the Hibernate JPA implementation. My problem is, that I can't use hardcoded Strings or constants in the xml-file you can see below.
It is also no possible to use a constant like in the name-attribute of the entity-tag. dst.ass1.jpa.util.Constants.T_CLASSROOM
this is the error message I get:
I don't know why I can use a constant in den name attribute of the entity tag, but not inside the column-tag.
I'm using IntelliJ IDEA 14.0.3
Hope you understand my problem.
regards.
For an attribute be used in an entity it must be "non-static".
If you map it in your XML, the JPA/Intellij will understand that you are trying to map an static attribute into the entity, and that is not possible.
According to James and Tomas Mapping Oracle XMLType on JPA (EclipseLink), XMLType fields are handled(mapping and persist) using #Customizer and DescriptorCustomizer.
I have few doubts on this approach.
Why the xmlField attribute don't need #Column anotation?
Why we go for DescriptorCustomizer for maapping the xmlField? This is only for mapping the fields? Or it can also persist that field?
DirectToXMLTypeMapping can be used for Object type? (Please refer: http://docs.oracle.com/cd/E15051_01/web.1111/b32441/relmapun.htm#CHDFIFEF)
As of Tomas post how that xmlField will be persist? Need to write Callback methods?
Please clarify me.Sorry if my question is pointless.
Please provide some links and documentation to understand the XMLType fields mapping in JPA(EclipseLink/Oracle Toplink).
XMLType is a specialized Oracle field type, and I'm guessing does not work well with a simple basic JPA mapping. EclipseLink has a custom DirectToXMLTypeMapping type that can handle conversions of the Oracle XMLType to/from a java String. In the example you've posted, the customizer is first removing the existing mapping that gets created through annotations for the "xmlField" attribute. This essentially wipes out any #Column information that might have been added. It then adds a new DirectToXMLTypeMapping containing all information required for the field, so there is no need to use annotations on the attribute.
Alternatively, you could define the #Column annotation and any other settings, and instead of just removing the mapping, you could get the DatabaseMapping object from the descriptor and pull the column name and other values from it to populate the new DirectToXMLTypeMapping.
A mapping is something that describes how the attribute in your entity relates to a database field. So it should handle retrieval as well as inserts/updates, converting it to/from the java type into/from the database type. In the link you posted, the entity's xmlType attribute mapping converts the Java String into an Oracle XMLType on persist, and reverses it when reading back from the DB.
I recently had a similar issue as this SOer had, where I was using Hibernate's Query#list object and getting compiler warnings for type safety.
Someone pointed out to me that I could use EntityManager#createQuery(String,Class<?>) instead of Query#list to accomplish the same thing but have everything genericized correctly.
I've been searching for examples of using Hibernate directly with EntityManager but so far no luck. So I ask: how can I use the EntityManager#createQuery method in lieu of the Query#list method when doing a SELECT from Hibernate?
Why is #ForceDiscriminator or its equivalent #DiscriminatorOptions(force=true) necessary in some cases of inheritance and polymorphic associations? It seems to be the only way to get the job done. Are there any reasons not to use it?
As I'm running over this again and again, I think it might help to clarify:
First, it is true that Hibernate does not require discrimination when using JOINED_TABLE mapping. However, it does require it when using SINGLE_TABLE. Even more importantly, other JPA providers mostly do require it.
What Hibernate actually does when performing a polymorphic JOINED_TABLE query is to create a discriminator named clazz on the fly, using a case-switch that checks for the presence of fields unique for concrete subclasses after outer-joining all tables involved in the inheritance-tree. You can clearly see this when including the "hibernate.show_sql" property in your persistence.xml. In my view this is probably the perfect solution for JOINED_TABLE queries, so the Hibernate folks are right to brag about it.
The matter is somewhat different when performing updates and deletes; here hibernate first queries your root-table for any keys that match the statement's where clause, and creates a virtual pkTable from the result. Then it performs a "DELETE FROM / UPDATE table WHERE pk IN pkTable" for any concrete class withing your inheritance tree; the IN operator causes an O(log(N)) subquery per table entry scanned, but it is likely in-memory, so it's not too bad from a performance perspective.
To answer your specific question, Hibernate simply doesn't see a problem here, and from a certain perspective they are correct. It would be incredibly easy for them to simply honour the #DiscriminatorValue annotations by injecting the discriminator values during entityManager.persist(), even if they do not actually use them. However, not honoring the discriminator column in JOINED_TABLE has the advantage (for Hibernate) to create a mild case of vendor lockin, and it is even defensible by pointing to superior technology.
#ForceDiscriminator or #DiscriminatorOptions(force=true) sure help to mitigate the pain a little, but you have to use them before the first entities are created, or be forced to manually add the missing discriminator values using SQL statements. If you dare to move away from Hibernate it at least costs you some code change to remove these Hibernate specific annotations, creating resistance against the migration. And that is obviously all that Hibernate cares about in this case.
In my experience, vendor lockin is the paradise every market leader's wildest dreams are about, because it is the machiavellian magic wand that protects market share without effort; it is therefore done whenever customers do not fight back and force a price upon the vendor that is higher than the benefits reaped. Who said that an Open Source world would be any different?
p.s, just to avoid any confusion: I am in no way affiliated to any JPA implementor.
p.p.s: What I usually do is ignore the problem until migration time; you can then formulate an SQL UPDATE ... FROM statement using the same case-switch-with-outer-joins trick Hibernate uses to fill in the missing discriminator values. It's actually quite easy once you have understood the basic principle.
Guys let me try to explain about #DiscriminatorOptions(Force=true).
Well , it is used in single table inheritence, i have recently used this in one of the scenario.
i have two entities which was mapped to single table. when i was trying to fetch the record for one entity i was getting list of result containg records from both the entities and this was my problem. To solve this problem i have used #DiscriminatorOptions(Force=true) which will create the predicate using Discriminator column with the specified value mapped to the corresponding entity.
so the query will be look like this after i used #DiscriminatorOptions(Force=true)
select *
from TABLE
where YOUR PREDICATE AND DiscriminatorColumn = DiscriminatorValue
I think this is more of my opinion but I think some will agree with me. I prefer the fact that Hibernate enables you to not use a discriminator. In several cases the discriminator isn't necessary.
For example, I have a Person entity which contains stuff like a name, a date of birth, etc. This entity can be used by several other entities like Employee or Customer. When I don't reference Person from other entities, but reference Employee or Customer instead, the discriminator isn't used as Hibernate is instructed to fetch either one.
#yannisf ForceDiscriminator is not the only solution to solve this issue.
You can do instanceof tests for each child class. Though this will be like hardcoding your classes in your code but is a cleaner way to solve the problem if the discriminator column is not populated.
This also helps your code avoid mixing jpa and hibernate annotations.
As pointed out by yannisf, instanceOf is kind of an antipattern in the OO world.
Another solution could be changing your entity mapping. Suppose an entity A has a refernce to a superclass B and B has child classes of type C1 and C2, the instead of A pointing to B, you can have C1 and C2 have a foreign key pointing to A. It all comes down to changing the entity design so as not to mix annotations.
Thanks
Vaibhav
It looks like that Hibernate started using LONG data type in version 3.5.5 (we upgraded from 3.2.7) instead of CLOB for the property of type="text".
This is causing problems as LONG data type in Oracle is an old outdated data type (see http://www.orafaq.com/wiki/LONG) that shouldn’t be used, and tables can’t have more than one column having LONG as a data type.
Does anyone know why this has been changed?
I have tried to set the Oracle SetBigStringTryClob property to true (as suggested in Hibernate > CLOB > Oracle :(), but that does not affect the data type mapping but only data transfer internals which are irrelevant to my case.
One possible fix for this is to override the org.hibernate.dialect.Oracle9iDialect:
public class Oracle9iDialectFix extends Oracle9iDialect {
public Oracle9iDialectFix() {
super();
registerColumnType(Types.LONGVARCHAR, "clob");
registerColumnType(Types.LONGNVARCHAR, "clob");
}
}
However this is the last resort - overriding this class is step closer to forking Hibernate which I would rather avoid doing.
Can anybody explain why this was done?
Should this be raised as a bug?
[UPDATE]: I have created https://hibernate.atlassian.net/browse/HHH-5569, let's see what happens.
It looks like the resolution to this issue is to use materialized_clob, at least that's what's being said by Gail Badner on HHH-5569.
This doesn't help me at all (and I left relevant comment about that) but might be helpful for someone else here. Anyway the bug is rejected and there is very little I can do about it but use overriden dialect :(
Can anybody explain why this was done? Should this be raised as a bug?
This has been done for HHH-3892 - Improve support for mapping SQL LONGVARCHAR and CLOB to Java String, SQL LONGVARBINARY and BLOB to Java byte[] (update of the documentation is tracked by HHH-4878).
And according to the same issue, the old behavior was wrong.
(NOTE: currently, org.hibernate.type.TextType incorrectly maps "text" to java.sql.Types.CLOB; this will be fixed by this issue and updated in database dialects)
You can always raise an issue but in short, my understanding is that you should use type="clob" if you want to get the property mapped to a CLOB.
PS: Providing your own Dialect and declaring it in your Hibernate configuration (which has nothing to do with a fork) is IMHO not a solution on the long term.
I cannot answer your question about why, but for Hibernate 6, it seems they're considering switching back to using CLOB