Hibernate Annotations with a legacy mapping table - java

I'm trying to convert part of a data-access layer into hibernate and running into some trouble getting the collection association right. I am using hibernate annotations, and have two Entities that are related (the base configuration here is working, just not the join). Some names have been obfuscated:
Spring 3, Hibernate 3, Java 6, MySQL 5.
Container has a List of Videos. Both of these classes are mapped in hibernate with annotations.
Unfortunately, the mapping table has an unusual schema. It looks like this:
container_mapping:
+-----------------+------------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+-------------------+-------+
| internal_id | int(10) unsigned | NO | PRI | | |
| external_id | varchar(255) | NO | PRI | | |
| mapping_type_id | int(4) unsigned | NO | PRI | | |
| creation_date | timestamp | NO | | CURRENT_TIMESTAMP | |
+-----------------+------------------+------+-----+-------------------+-------+
internal_id maps to container.id and external_id maps to video.id when mapping_type_id = 2
The query to pull back by id:
select * from container c, container_mapping cm, video v where cm.mapping_type_id=2 and c.episode_id = cm.internal_id and cm.external_id = v.id and c.episode_id=?;
I can't seem to find a good example to having a collection join WITH a join field having a specific value. At this point I'm grasping at straws. The (incomplete) version is here:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(name="container_mapping",
joinColumns = #JoinColumn(name="external_id"))
private List<Video> videos = Lists.newArrayList();
Anyone have any pointers on how to proceed?

You could try mapping the videos collection this way:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(name="container_mapping",
joinColumns = #JoinColumn(name="internal_id"),
inverseJoinColumns = #JoinColumn(name="external_id")
#WhereJoinTable(clause="mapping_type_id=2")
private List<Video> videos = Lists.newArrayList();
The idea behind this mapping is following:
The joinColumns parameter of the #JoinTable annotation is used to configure the column, which stores the value of container id.
The column storing the video id is configured by using the inverseJoinColumns parameter of the #JoinTable annotation.
The #WhereJoinTable annotation can be used to restrict the rows selected from the join table. The clause parameter must be written by using SQL. In this case it used to restrict the value of mapping_type_id, which must be 2.

Related

Calling/mapping another entity in getter with hibernate

Consider this simple database schema:
User Relation Property
+-------------+ +--------------+ +---------------+
| -user_id | | -relation_id | | -property_id |
| | | -source_id | | |
| | | -target_id | | |
| | | -type | | |
+-------------+ +--------------+ +---------------+
The relation table contains multiple source and target ids (as STRING) from another tables and "connect" dynamically difficult types of entities. So the source_id can be a user_id and target_id property_id, but also other entities and vice versa.
Now I want in User the properties over the relation table when type is property.
#OneToMany(mappedBy = "source", fetch = FetchType.EAGER)
#Where(clause = "type = 'PROPERTY'")
#ElementCollection
private Set<Relation> properties;
This works perfectly fine. I receive the relation entity with the property id as string in target_id.
Is it possible to resolve this and extend the getter, so that i receive the real property entity?
Like calling the propertyRepository within the user entity. As a newbie in java and jpa/hibernate im a bit lost here.

Define an entity via a custom SQL query in Hibernate 5.1

I'm working with a non normalized 3rd party database meaning I cannot change the schema. I'm trying to map the tables to JPA entities using Hibernate 5.1
There are 2 simple tables A and B:
| A_ID(pk) | | B_ID(pk) |
------------- -------------
| 1 | | 1 |
------------- | 2 |
-------------
Table C has a composite primary key and has a Many-To-One relation to Table A:
| A_ID(pk&fk) | QUANTITY(pk) | VALID_FROM(pk) |
---------------------------------------------------
| 1 | 1 | 2017-05-21 |
| 1 | 1 | 2018-01-01 |
| 1 | 2 | 2017-05-21 |
Table D has a composite primary key:
| A_ID(pk&fk) | QUANTITY(pk) | VALID_FROM(pk) | B_ID(pk&fk) |
--------------------------------------------------------------------
| 1 | 1 | 2018-01-21 | 1 |
| 1 | 2 | 2018-01-21 | 1 |
| 1 | 2 | 2018-05-01 | 2 |
the VALID_FROM column is not part of the join condition between the tables and can take up any value.
I'm trying to set up a relation between Table C and D but because of the VALID_FORM primary key component they cannot be modelled with Many-To-One. And since there is no join table they cannot be modelled with Many-To-Many either.
The best solution would be to create a view like
CREATE VIEW C_NORM AS
SELECT DISTINCT A_ID, QUANTITY
FROM TABLE_C;
which would produce view C_NORM:
| A_ID(pk&fk) | QUANTITY(pk) |
----------------------------------
| 1 | 1 |
| 1 | 2 |
Creating the C_NORM entity on this view could have
a One-To-Many relation with Table C
and another One-To-Many relation with Table D
but I cannot change the schema thus I cannot create a new view.
Is there any way to define an entity as a class with annotations that is basically based on a native SQL query rather than a view or table in the DB?
No that's not possible and it doesn't make sense.
Entities are for update, insert and delete. If you don't want to do any of these operations you shouldn't use entities.
You can use the #SqlResultSetMapping to map a result of a native query to a class
Query q = em.createNativeQuery(
"SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
"FROM Customer c " +
"JOIN Orders o ON o.cid = c.id " +
"GROUP BY c.id, c.name",
"CustomerDetailsResult");
#SqlResultSetMapping(name="CustomerDetailsResult",
classes={
#ConstructorResult(targetClass=com.acme.CustomerDetails.class,
columns={
#ColumnResult(name="id"),
#ColumnResult(name="name"),
#ColumnResult(name="orderCount"),
#ColumnResult(name="avgOrder", type=Double.class)})
})
Or alternatively use QLRM: https://github.com/simasch/qlrm

Spring JPA OneToMany with composite Key mapping

I am having issues mapping the following using spring JPA. Let's say I have a order table which has two primary keys. one is a foreign key to customer and another is a foreign key to order Type as seen below:
Customer
+----+------+--+
| id*| name | |
+----+------+--+
| 1 | Joe | |
+----+------+--+
Order
+------------+-------------+
| customerId | orderTypeId |
+------------+-------------+
| 1 | 1 |
+------------+-------------+
OrderType
+----+--------+
| id | type |
+----+--------+
| 1 | online |
+----+--------+
the idea is each customer has a one to many relationship with Orders. the primary key of orders is a combination of the two foreign keys.
any help would be appreciated.

Why does Hibernate insert a parent row with a foreign key without inserting the child row?

I'm hoping someone has run into this problem before and can help me out. Basically, Hibernate is inserting a parent row (with an ID pointing to a child row), but not inserting that child row with the associated ID, which leaves the database in a bad state. Here's an example of the exception that's thrown when Hibernate tries to load the improperly saved object:
27 Jun 2011 13:55:31,380 ERROR [scheduler_Worker-4] -
Job DEFAULT.queryScrubJobDetail threw an unhandled Exception:
org.springframework.scheduling.quartz.JobMethodInvocationFailedException:
Invocation of method 'doIt' on target class [XXX] failed; nested exception is
org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException:
No row with the given identifier exists:
[XXX.DataProviderTransaction#60739703]; nested exception is org.hibernate.ObjectNotFoundException:
No row with the given identifier exists:
[com.idology.persist.DataProviderTransaction#2]
This part of the application has three entities:
Query, which is a parent of DataProviderTransactionReference and DataProviderTransaction
DataProviderTransaction, which is a child of Query and a parent of DataProviderTransactionReference
DataProviderTransactionReference, which has foreign keys pointing to DataProviderTransaction and Query
Here are the mappings:
From Query:
#OneToMany(mappedBy = "query", cascade =
{ CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
#JoinColumn(name = "query_id")
public List<DataProviderTransactionReference> getDataProviderTransactionReferences()
From DataProviderTransaction:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "query_id")
public Query getQuery()
From DataProviderTransactionReference:
#ManyToOne(cascade =
{ CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER)
#JoinColumn(name = "data_provider_transaction_id")
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
public DataProviderTransaction getDataProviderTransaction()
{
return mDataProviderTransaction;
}
The schema looks like this (leaving out the queries table since it has no foreign keys):
data_provider_transaction
+------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| query_id | bigint(20) | YES | MUL | NULL | |
+------------------+---------------+------+-----+---------+----------------+
data_provider_txn_refs
+------------------------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------------+------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| created_at | datetime | YES | | NULL | |
| data_provider_transaction_id | bigint(20) | YES | MUL | NULL | |
| query_id | bigint(20) | YES | MUL | NULL | |
+------------------------------+------------+------+-----+---------+----------------+
So once we're done running a query (represented by the Query object), we save it using Spring and Hibernate using the following:
getHibernateTemplate().saveOrUpdate(aQuery);
The Query is saved along with the associated DataProviderTransaction and DataProviderTransactionReference entities. Except that sometimes it saves a Query and a DataProviderTransactionReference without the associated DataProviderTransaction. It does put an ID in the data_provider_transaction_id but it points to a row that does not exist in the data_provider_transaction table.
The next step is to add a foreign key constraint to cause the problem to occur when we do the initial save rather than when we try to load the object later.
We're using Spring 2.5.6, Hibernate 3.3.2, and MySQL 5.0. I've seen the problem occur over the years with earlier versions of Spring and Hibernate, though.
Anyone ever seen/solved this problem?
This sounds like a problem with your id allocation with MySQL. Hibernate can get confused if the generators are not declared correctly, or if you are doing strange things with your code.
Do you have orphan DataProviderTransactions or DataProviderTransactionReferences, A DataProviderTransaction which has a query id which does not exist, or point to the wrong Query?
Are your generators declared as identity for your ids? (see Chapter 5. Basic O/R Mapping, section 5.1.4 id. Normally, this should be enough, but there may be other things you're doing which confuse hibernate.
So, for instance:
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
To really track this down, you need to know why this is happening, who is inserting these rows. You need a foreign key constraint in the database. It's also possible that something is deleting the DataProviderTransaction and the database isn't complaining because there is no foreign key.

Hibernate bit array to entity mapping

I am trying to map a normalized Java model to a legacy database schema using Hibernate 3.5. One particular table encodes a foreign keys in a one-to-many relationship as a bit array column.
Consider tables person and club that describes people's affiliations to clubs:
person: .----.------. club: .----.---------.---------------------------.
| id | name | | id | name | members | binary(members) |
|----+------| |----+---------|---------+-----------------|
| 1 | Bob | | 10 | Cricket | 0 | 000 |
| 2 | Joe | | 11 | Tennis | 5 | 101 |
| 3 | Sue | | 12 | Cooking | 7 | 111 |
'----'------' | 13 | Golf | 3 | 100 |
'----'---------'---------'-----------------'
So hopefully it is clear that person.id is used as the bit index in the bit array club.members:
.---.---.---.
| S | J | B |
| u | o | o |
| e | e | b |
|---+---+---|
| 1 | 0 | 1 |
'---'---'---'
In this example the members column tells us that:
no one is a member of Cricket --- no flags set
Bob/Sue -> Tennis --- flags at positions 1 and 3 are set
Bob/Sue/Joe -> Cooking --- flags at positions 1, 2 and 3 are set
Sue -> Golf --- flag at position 3 is set
Now, for this example a join table could've been used instead which would simplify matters and avoid many potential issues - e.g: the maximum range of members placing an upper bound on the number of people rows. However, I am stuck with this schema and it seems that there were factors in favour of using a bit array column way back when.
In my Java domain I'd like to model this schema with entities like so:
class Person {
private int id;
private String name;
...
}
class Club {
private Set<Person> members;
private int id;
private String name;
...
}
I am assuming that I must use a UserType implementation but have been unable to find any examples where the items described by the user type are references to entities - not literal field values - or composites thereof. Additionally I am aware that I'll have to consider how the person entities are fetched when a club instance is loaded.
Can anyone tell me how I can tame this legacy schema with Hibernate?
Update
I have recently had to revisit this type of mapping in a legacy database. This time around it became apparent that our equivalent of the members table was actually a static set and could be hardcoded as an Enum. With this simplification it was fairly straightforward to implement a Hibernate UserType that converted between the bit array and a set of enums.
I've never faced this situation but I think that you'll need to implement a custom UserCollectionType (see chapter 5.2.3. Custom value types in Hibernate Core documentation), the UserCollectionType being an extension point which may be used to support any damn collection and collection semantics you like.
I'm not sure how well they are supported by annotations though (according to HHH-4417, you may have to use a hack). Using hbm.xml for this would be a good idea here IMO.
Some more pointers/discussions:
https://forum.hibernate.org/viewtopic.php?f=9&t=938522&start=0
http://www.javalobby.org/java/forums/m91832311.html (different case but may give you an idea of how to start)

Categories