I've been trying to do a simple one to many object binding in DataNucleus JDO. It's just two classes (i stripped a simple fields):
#PersistenceCapable(table="ORDER",schema="mgr")
public class Order {
#PrimaryKey(column="id")
#Persistent(valueStrategy=IdGeneratorStrategy.NATIVE,column="id")
private Long id;
#Persistent(defaultFetchGroup="false",column="customer_id")
#Element(column="customer_id")
private Customer customer;
}
And a class Customer having a list of orders
#PersistenceCapable(table="customer",schema="mgr",identityType=IdentityType.DATASTORE)
#DatastoreIdentity(strategy=IdGeneratorStrategy.NATIVE)
public class Customer {
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.NATIVE,column="id")
private Long id;
#Persistent(mappedBy="customer")
private List<Order> orders;
}
The database table setup is extremely simple(a table for customer and a table for orders with a foreign key (customer_id) referencing customer). Yet, when i try to insert some orders for customer i receive an error
javax.jdo.JDODataStoreException: Insert of object
"test.Order#17dd585" using statement "INSERT INTO
ORDER
(USER_COMMENT,ORDER_DATE,STATUS,CUSTOMER_ID,ORDERS_INTEGER_IDX)
VALUES (?,?,?,?,?)" failed : Unknown column 'ORDERS_INTEGER_IDX' in
'field list'
Somehow DataNucleus is assuming, there is a column ORDERS_INTEGER_IDX (such column does not exist in the database). The only idea, that came to my mind is http://www.datanucleus.org/products/datanucleus/jdo/metadata_xml.html
In some situations DataNucleus will add a special datastore column to
a join table so that collections can allow the storage of duplicate
elements. This extension allows the specification of the column name
to be used. This should be specified within the field at the
collection end of the relationship. JDO2 doesnt allow a standard place
for such a specification and so is an extension tag.
So cool! 'in some situations'. I have no idea how to make my situation not to be a subset of 'some situations' but I have no idea, how to get this working. Perhaps someone has allready met the "INTEGER_IDX" problem? Or (it is also highly possible) - im not binding the data correctly :/
So you create the schema yourself. Your schema is inconsistent with metadata. You run persistence without validating your metadata against schema, and an exception results. DataNucleus provides you with SchemaTool to create or validate the schema against your metadata, so that would mean that you can detect the problem.
You're using an indexed list, so it needs an index for each element (or how else is it to know what position an element is in?). How can it assume there is an index? well it's a thing called the JDO spec (publically available), which defines indexed lists. If you don't want positions of elements storing then don't use a List (the Java util class for retaining the position of elements) ... so I'd suggest using a Set since that doesn't need position info (hence no index).
You also have a class marked as datastore identity, and then have a primary-key. That is a contradiction ... you have one or the other. The docs define all of that, as well as how to have a 1-N List relation ("JDO API" -> "Mapping" -> "Fields/Properties" -> "1-N Relations" -> "Lists" or "Sets")
Related
Please don't ask me why I need to do this, as even if I think I could find another way to solve my specific problem, I want to understand HQL and its limits more.
Given the following entities
#Entity
class Child {
private String someAttribute1;
.....
private String someAttributeN;
#ManyToOne(EAGER)
private Parent parent;
}
class Parent {
private String someParent1;
....
private String someParentN;
}
If I select Child then Hibernate automatically fetches all columns from Child and Parent in a single joined SQL, and that is the typical desired case.
Sometimes I know that, for entities mapped with a large number of columns, I need only a subset.
If I select item.someAttribute1 as someAttribute1, item.someAttribute2 as someAttribute2, item.someAttribute3 as someAttribute3 from Child item etc. tied to a ResultTransformer I can let Hibernate return me only 3 columns from the SQL, or more columns if I list them. OK, that is cool and works like a charm.
However if I need to fetch only, say, 3 columns from Child and 2 from Parent, while the rest can be null, and materialize a Child entity with its relationship, I cannot write the following
select item.someAttribute1 as someAttribute1, item.someAttribute2 as someAttribute2, item.someAttribute3 as someAttribute3, item.parent.someParent1 as parent.someParent1, item.parent.someParent2 as parent.someParent2 from Child item left join item.parent
The above does not work because Hibernate does not allow an alias to be composed. It disallows me to use as parent.someName clause because aliases should probably be flat.
Just to tell a counter example, in languages such as LINQ the problem does not apply
from Child c in children
select new Child {
SomeAttribute1 = c.someAttribute1,
SomeAttribute2 = c.someAttribute2,
Parent = new Parent {
Attribute1 = c.Parent.Attribute1,
.......
}
}
With the above statement, Entity Framework will only fetch the desired columns.
I don't want to make comparison or criticism between Hibernate for Java and Entity Framework for C#, absolutely.
I only have the need to fetch a subset of the columns that compose an entity with a #ManyToOne relationship, in order to optimize memory and bandwidth usage. Some columns from the child entity and some from the parent.
I just want to know if and how is it possible in Hibernate to achieve something like that. To populate parent attribute in the result set with an object of class Parent that is populated with only a subset of columns (the rest being null is no problem). I am using ResultTransformers happily
There are two problems with it.
Hibernate doesn't allow to use nested aliases like as parent.someName in HQL. It produces a parsing error. But you can use nested aliases with Criteria using Projections.property("parent.someName").
Hibernate doesn't have a result transformer to populate result objects using nested aliases.
You can use Criteria requests with a custom result transformer as described here
How to transform a flat result set using Hibernate
In a project I'm working on, there seems to be a problem mapping a (Hash)Map to the database using JPA.
The Map (named 'racers', within the entity 'Race') consists of key-value-pairs < User, Racestats >, both custom entities in JEE.
The map is annotated by "#ElementCollection".
When trying to persist the map to the database, an error is given: "Data truncation: Data too long for column 'RACERS'".
When checking the database, we see a table 'Race_RACERS' is created, which consists of three columns: two bigints (representing the id of the Race object and the User object) and one varchar, which contains the Racestats object.
Of course, this last column should also contain references to the Racestats, instead of embedding these Racestats objects.
We have already tried fixing the issue using several other annotations, but none of them seem to work.
Could anyone please provide us with the correct syntax to persist our objects.
Keys will obviously be unique in each map, but within different Race objects, the Maps could contain the same key.
No 2 values will ever be the same. Even within different Race objects, maps will never contain the same value.
I don't have all the information about your use cases, but it looks to me like it would be simpler to put the User reference inside the Racestats entity, e.g.:
#Entity
public class Race {
#OneToMany(mappedBy="race")
Set<Racestats> racestats;
}
#Entity
public class Racestats {
#ManyToOne
User user;
#ManyToOne
Race race;
// Other race stats fields
...
}
I have a model class that references another model class and seem to be encountering an issue where the #OneToOne annotation fixes one problem but causes another. Removing it causes the inverse.
JPA throws "multiple assignments to same column" when trying to save changes to model. The generated SQL has duplicate columns and I'm not sure why.
Here's a preview of what the classes look like:
The parent class references look like this:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne
public Location location;
}
The child Location class has an id field and a few other text fields -- very simple:
public class Location {
public Integer id;
public String name;
}
When I attempt to perform a save operation, does anyone know why JPA is creating an insert statement for the Appliance table that contains two fields named "location_id"?
I need to annotate the reference to the child class with #OneToOne if I want to be able to retrieve data from the corresponding database table to display on screen. However, If I remove #OneToOne, the save works fine, but it obviously won't load the Location data into the child object when I query the db.
Thanks in advance!
It appears you did not define an #InheritanceType on the parent Class. Since you did not, the default is to combine the the parent and the child class into the same Table in the Single Table Strategy.
Since both entities are going into the same table, I think that #OneToOne is trying to write the id twice - regardless of which side it is on.
If you want the parent to be persisted in its own table, look at InheritanceType.JOINED.
Or consider re-factoring so that you are not persisting the parent separately as JOINED is not considered a safe option with some JPA providers.
See official Oracle Documentation below.
http://docs.oracle.com/javaee/7/tutorial/doc/persistence-intro002.htm#BNBQR
37.2.4.1 The Single Table per Class Hierarchy Strategy
With this strategy, which corresponds to the default InheritanceType.SINGLE_TABLE, all classes in the hierarchy are mapped to a single table in the database. This table has a discriminator column containing a value that identifies the subclass to which the instance represented by the row belongs.
In OpenJPA, according to the docs (http://openjpa.apache.org/builds/1.0.1/apache-openjpa-1.0.1/docs/manual/jpa_overview_mapping_field.html), section 8.4, the foreign key column in a one-to-one mapping:
Defaults to the relation field name, plus an underscore, plus the name
of the referenced primary key column.
And the JPA API seems to concur with this (http://docs.oracle.com/javaee/6/api/javax/persistence/JoinColumn.html)
I believe this means that in a one-to-one mapping, the default column name for properties in a dependent class is parentClassFieldName_dependentClassFieldName (or location_id in your case). If that's the case, the location_id column you are defining in your Appliance class is conflicting with the location_id default column name which would be generated for your Location class.
You should be able to correct this by using the #Column(name="someColumnName") annotation and the #JoinColumn annotation on your #OneToOne relationship to force the column name to be something unique.
Ok gang, I figured it out.
Here's what the new code looks like, followed by a brief explanation...
Parent Class:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="location_id", referencedColumnName="id")
public Location location;
}
Child Class:
public class Location {
public Integer id;
public String name;
}
The first part of the puzzle was the explicit addition of "cascade = CascadeType.ALL" in the parent class. This resolved the initial "multiple assignments to same column" by allowing the child object to be persisted.
However, I encountered an issue during update operations which is due to some sort of conflict between EBean and JPA whereby it triggers a save() operation on nested child objects rather than a cascading update() operation. I got around this by issuing an explicit update on the child object and then setting it to null before the parent update operation occurred. It's sort of a hack, but it seems like all these persistence frameworks solve one set of problems but cause others -- I guess that's why I've been old school and always rolled my own persistence code until now.
I have a simple jpa entity 'ApplicationForm' with a one to many list in it:
#OneToMany(cascade=CascadeType.REMOVE, mappedBy="textQuestion")
private List<Dictionary> questions;
The variable Dictionary contained in ApplicationForm is just another plain entity with just the text of the question.
The corresponding database table mapped by Dictionary is:
'locale' 'text' 'formId'
en my question 123
it mia domanda 123
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
That would be easy enough to do with standard sql, but I cannot translate in hql.
If not possible, could you suggest an alternative way ? I have tried to manually iterate the Dictionary questions list and remove the not required locale, but is not really elegant, and also I got a jpa/hibernate error.
I hope I made myself clear, and code supplied is enough.
thanks
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
Not with standard JPA. But Hibernate allows to apply arbitrary filters to a collection load during a given session. From the Hibernate Annotations Reference Guide:
2.4.8. Filters
Hibernate has the ability to apply
arbitrary filters on top of your data.
Those filters are applied at runtime
on a given session. First, you need to
define them.
#org.hibernate.annotations.FilterDef
or #FilterDefs define filter
definition(s) used by filter(s) using
the same name. A filter definition has
a name() and an array of
parameters(). A parameter will allow
you to adjust the behavior of the
filter at runtime. Each parameter is
defined by a #ParamDef which has a
name and a type. You can also define a
defaultCondition() parameter for a
given #FilterDef to set the default
condition to use when none are defined
in each individual #Filter. A
#FilterDef(s) can be defined at the
class or package level.
We now need to define the SQL filter
clause applied to either the entity
load or the collection load. #Filter
is used and placed either on the
entity or the collection element
#Entity
#FilterDef(name="minLength", parameters=#ParamDef( name="minLength", type="integer" ) )
#Filters( {
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
#Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
When the collection use an association
table as a relational representation,
you might want to apply the filter
condition to the association table
itself or to the target entity table.
To apply the constraint on the target
entity, use the regular #Filter
annotation. However, if you wan to
target the association table, use the
#FilterJoinTable annotation.
#OneToMany
#JoinTable
//filter on the target entity table
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
//filter on the association table
#FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
public Set<Forest> getForests() { ... }
See also
Chapter 17. Filtering data In the Hibernate Core Reference Documentation.
Hibernate3 Filters
In legacy database tables we have numbered columns like C1, C2, C3, C100 or M1, M2, M3, M100.
This columns represent BLOB data.
It is not possible to change anything it this database.
By using JPA Embeddable we map all of the columns to single fields. And then during embedding we override names by using 100 override annotations.
Recently we have switched to Hibernate and I've found things like UserCollectionType and CompositeUserType. But I hadn't found any use cases that are close to mine.
Is it possible to implement some user type by using Hibernate to be able to map a bundle of columns to a collection without additional querying?
Edit:
As you probably noticed the names of columns can differ from table to table. I want to create one type like "LegacyArray" with no need to specify all of the #Columns each time I use this type.
But instead I'd use
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "A"),
#Parameter(name = "size", value = "128")
})
List<Integer> legacyA;
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "B"),
#Parameter(name = "size", value = "64")
})
List<Integer> legacyB;
I can think of a couple of ways that I would do this.
1. Create views for the collection information that simulates a normalized table structure, and map it to Hibernate as a collection:
Assuming your existing table is called primaryentity, I would create a view that's similar to the following:
-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)
Now from Hibernate's perspective, childentity is just a normalized table that has a foreign key to primarykey. Mapping this should be pretty straight forward, and is covered here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/collections.html
The benefits of this approach:
From Hibernate's point of view, the tables are normalized, it's a fairly simple mapping
No updates to your existing tables
The drawbacks:
Data is read-only, I don't think your view can be defined in an updatable manner (I could be wrong)
Requires change to the database, you may need to create lots of views
Alternately, if your DBA won't even let you add a view to the database, or if you need to perform updates:
2. Use Hibernate's dynamic model mapping facility to map your C1, C2, C3 properties to a Map, and have some code you your DAO layer do the appropriate conversation between the Map and the Collection property:
I have never done this myself, but I believe Hibernate does allow you to map tables to HashMaps. I'm not sure how dynamically Hibernate allows you to do this (i.e., Can you get away with simply specifying the table name, and having Hibernate automatically map all the columns?), but it's another way I can think of doing this.
If going with this approach though, be sure to use the data access object pattern, and ensure that the internal implementation (use of HashMaps) is hidden from the client code. Also be sure to check before writing to the database that the size of your collection does not exceed the number of available columns.
The benefits of this approach:
No change to the database at all
Data is updatable
O/R Mapping is relatively simple
The drawbacks:
Lots of plumbing in the DAO layer to map the appropriate types
Uses experimental Hibernate features that may change in the future
Personally, I think that design sounds like it breaks first normal form for relational databases. What happens if you need C101 or M101? Change your schema again? I think it's very intrusive.
If you add Hibernate to the mix it's even worse. Adding C101 or M101 means having to alter your Java objects, your Hibernate mappings, everything.
If you have 1:m relationships with C and M tables, you'd be able handle the cases I just cited by adding additional rows. Your Java objects contain Collection<C> or Collection<M>. Your Hibernate mappings are one-to-many that don't change.
Maybe the reason that you don't see any Hibernate examples to match your case because it's a design that's not recommended.
If you must, maybe you should look at Hibernate Component Mapping.
UPDATE: The fact that this is legacy is duly noted. My point in bringing up first normal form is as much for others who might find this question in the future as it is for the person who posted the question. I would not want to answer the question in such a way that it silently asserted this design as "good".
Pointing out Hibernate component mapping is pertinent because knowing the name of what you're looking for can be the key when you're searching. Hibernate allows an object model to be finer grained than the relational model it maps. You are free to model a denormalized schema (e.g., Name and Address objects as part of a larger Person object). That's just the name they give such a technique. It might help find other examples as well.
Sorry if I'm misunderstanding your problem here, I don't know much about Hibernate. But couldn't you just concatenate during selection from database to get something like what you want?
Like:
SELECT whatever
, C1||C2||C3||C4||...||C100 AS CDATA
, M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...
(Of course, the concatenation operator differs between RDBMSs.)
[EDIT] I suggest to use a CompositeUserType. Here is an example. There is also a good example on page 228f in the book "Java Persistence With Hibernate".
That allows you to handle the many columns as a single object in Java.
The mapping looks like this:
#org.hibernate.annotations.Columns(columns = {
#Column(name="C1"),
#Column(name="C2"),
#Column(name="C3"),
...
})
private List<Integer> c;
Hibernate will load all columns at once during the normal query.
In your case, you must copy the int values from the list into a fixed number of columns in nullSafeSet. Pseudocode:
for (int i=1; i<numColumns; i++)
if (i < list.size())
resultSet.setInt(index+i, list.get(i));
else
resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());
In nullSafeGet you must create a list and stop adding elements when a column is NULL. For additional safety, I suggest to create your own list implementation which doesn't allow to grow beyond the number of columns (inherit from ArrayList and override ensureCapacity()).
[EDIT2] If you don't want to type all the #Column annotations, use a code generator for them. That can be as simple as script which you give a name and a number and it prints #Column(...) to System.out. After the script ran, just cut&paste the data into the source.
The only other solution would be to access the internal Hibernate API to build that information at runtime but that API is internal, so a lot of stuff is private. You can use Java reflection and setAccessible(true) but that code probably won't survive the next update of Hibernate.
You can use UserTypes to map a given number of columns to any type you wish. This could be a collection if (for example) for collections are always bounded in size by a known number of items.
It's been a while (> 3 years) since I used Hibernate so I'm pretty rusty but I recall it being very easy to do; your BespokeUserType class gets passed the ResultSet to hydrate your object from it.
I too have never used Hibernate.
I suggest writing a small program in an interpreted language (such as Python) in which you can execute a string as if it were a command. You could construct a statement which takes the tedious work out of doing what you want to do manually.