In which direction Hibernate orm-mapping works - java

I have a rather simple (probably) question, but somehow struggling to find an answer.
How does hibernate map nested entities to java objects? Does it starts its mapping from high-level entities and stops on encountering null-values in ResultSet, or it starts from the lowest-level entities and check all of the hierarchy?
The first path seems to be more natural, but yet I didn't find a concrete answer or any way to configure this
EDIT
By nested entities I suppose something like this:
#Entity
public class A {
public Long id;
public String foo;
}
#Entity
public class B {
public Long Id;
#OneToOne
#JoinColumn(name = "aId")
public A bar;
}
Where bar field is nested entity for B
One more edit
By ORM-mapping I meant mapping on select query call - so, if table B has no records and we execute any select-query to find entities of B, does Hibernate atempt to find and map entities of A ? Futhermore, if we have
#Entity
public class C {
public Long Id;
#OneToOne
#JoinColumn(name = "bId")
public B foobar;
}
and yet there are no records in table B, some in C and some in A, when we use any find methods for C, does Hibernate attempt to find and map entities of A?

Hibernate ORM won't create a proxy for *-to-one association that can be null (#OneToOne(optional=false)). If the element is missing in the db it will be null when the entity is created. So, using your example, C.foobar will be null if there is no row on the table B in the db. The same applies to the other *-to-one associations.
If the element is not optional, it might load it lazily and therefore assign a proxy to it.
To expand a bit about your question.
This is a one-to-one association. More in general, what you call nested is an association in Hibernate ORM. You can find all the details about these types of mapping in the Hibernate ORM documentation.
For your specific example:
#Entity
public class A {
#Id
#Column(name = "aId")
public Long id;
public String foo;
}
#Entity
public class B {
#Id
public Long id;
#OneToOne
#JoinColumn(name = "aId")
public A bar;
}
Hibernate ORM will create the following tables in Postgres:
create table A (aId int8 not null, foo varchar(255), primary key (aId))
create table B (Id int8 not null, aId int8, primary key (Id))
alter table if exists B add constraint FK3mifipyyn4ao31rn7kftqknuc foreign key (aId) references A
Basically:
Table A with columns aid and foo
Table B with columns id and aid
Table B column aid has a foreign constraint to Table A column aid
You can find all the details about one-to-one mapping in the Hibernate ORM documentation.
If you run the HQL query from B, Hibernate ORM will run the following SQL:
1. select * from B;
2. select * from A a.where a.aid = ?
if the HQL is from C
1. select * from C;
2. select * from B b left outer join A a on b.aId = a.aId where b.id=?;
In both cases, the second query only runs if there are results for the first one.
Keep in mind that this is a basic example and the type of queries executed
will change based on the configuration and the mapping of the entities.
You can actually tweak this behaviour with small changes to the mapping (Lazy/Eager fetching, optional, using bidirectional associations, ...) or the queries. I'm not going through all the possible mappings and you should write your own tests and check what SQL is logged.
Hibernate ORM might also skip some queries if some of the entities or the query has been cached.

Related

Spring Data JPA get entity foreign key without causing the dependent entity lazy load

I have an #Entity A that references another entity B using OneToOne relation ship. I fetch entity A using spring data JpaRepository
A a = aRepository.findById(1);
int b_id = a.getB().getId();
As you can see I need to query ID of the B table, however in order to do that, I need to call getter of the B table, which will cause lazy-loading the B table itself. I do not want to do that because the only thing I need is the get ID, nothing else, and that ID is present in the first A table.
Is there any trick that will help me to get ID of the dependent table without triggering new query?
UPDATE
#Entity
class A {
#Id
private Long id;
#OneToOne
private B b;
}
#Entity
class {
#Id
private Long id;
}
Without looking at the entity mapping, I suspect, your entity classes might be using hibernate annotations on the field. With this if you call even the getId() method as in a.getB().getId() on the entity it will result in initializing the proxy (i.e., B object) and hits the database to fetch it.
So if the intent is only to get the id of the entity you can place the hibernate annotations on the getter methods instead. This doesn't result initializing the proxy (B object) to return the id. Although accessing any property other than id will result in hitting the database.
Have a look at related bug at HHH-3718
So, try using property/getter AccessType instead of field access. As an example instead of placing the annotations on field
#Id
#GeneratedValue(...)
private long id;
place them on the getters
#Id
#GeneratedValue(...)
public long getId() { ... }
Make sure you make similar changes to all the fields of B entity. Although you can explore #Access(AccessType.PROPERTY/FIELD) later.
There is already a related bug HHH-3718 regarding this behavior.
And a related topic on hibernate forum regarding field vs property access type that might be of interest for you Field Vs Property access
Posting your entities classes would help, if this doesn't resolve the issue.

#GeneratedValue polymorphic abstract superclass over MySQL

In a Spring MVC application using Hibernate and MySQL, I have an abstract superclass BaseEntity that manages the values of the IDs for all the other entities in the model. The id field uses #GeneratedValue. I am encountering a problem whenever my code tries to save any of the subclasses that extend BaseEntity. The problem comes with the choice of GenerationType for the #GeneratedValue.
At every place in my code where a subclass of BaseEntity tries to save to the underlying MySQL database, I get the following error:
ERROR SqlExceptionHelper - Table 'docbd.hibernate_sequences' doesn't exist
I have read many postings about this on SO and on google, but they either deal with other databases (not MySQL) or they do not deal with abstract superclasses. I cannot solve the problem by using GenerationType.IDENTITY because I am using an abstract superclass to manage id fields for all entities in the model. Similarly, I cannot use GenerationType.SEQUENCE because MySQL does not support sequences.
So how do I solve this problem?
Here is the code for BaseEntity.java:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
protected Integer id;
public void setId(Integer id) {this.id = id;}
public Integer getId() {return id;}
public boolean isNew() {return (this.id == null);}
}
Here is an example of the code for one of the entities that extends BaseEntity:
#Entity
#Table(name = "ccd")
public class CCD extends BaseEntity{
//other stuff
}
Here is the DDL:
CREATE TABLE IF NOT EXISTS ccd(
id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
#other stuff
)engine=InnoDB;SHOW WARNINGS;
Here is the JPQL code in the DAO:
#Override
#Transactional
public void saveCCD(CCD ccd) {
if (ccd.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist CCD ]]]]]]]]]]]]]]]]]]]]");
this.em.persist(ccd);
this.em.flush();
}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge CCD [[[[[[[[[[[[[[[[[[[[[");
this.em.merge(ccd);
this.em.flush();
}
}
EDIT:
The reason I cannot use #MappedSuperClass in this situation is that I need to have ManyToOne relationships that allow for multiple subtypes to be used interchangeably. Look at the AccessLog class below as an example. It has an actor_entity and a target_entity. There can be many types of actor entities and many types of target entities, but they all inherit from BaseEntity. This inheritance enables the underlying accesslogs data table in MySQL to just have one actor_entity_id field and just one target_entity_id field instead of having to have several fields for each. When I change #Entity above BaseEntity to #MappedSuperClass, a different error gets thrown indicating that AccessLog cannot find BaseEntity. BaseEntity needs #Entity annotation in order for AccessLog to have polymorphic properties.
#Entity
#Table(name = "accesslogs")
public class AccessLog extends BaseEntity{
#ManyToOne
#JoinColumn(name = "actorentity_id")
private BaseEntity actor_entity;
#ManyToOne
#JoinColumn(name = "targetentity_id")
private BaseEntity target_entity;
#Column(name="action_code")
private String action;
//getters, setters, & other stuff
}
SECOND EDIT:
As per JBNizet's suggestion, I created a hibernate_sequences table as follows:
CREATE TABLE IF NOT EXISTS hibernate_sequences(
sequence_next_hi_value int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
)engine=InnoDB;SHOW WARNINGS;
But now I am getting the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause'
Here is the hibernate sql causing the error, followed by the next 2 lines of the stack trace:
Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'BaseEntity' for update
ERROR MultipleHiLoPerTableGenerator - HHH000351: Could not read or init a hi value
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'sequence_name' in 'where clause'
How do I resolve this?
What a mess... AUTO_INCREMENT is MySQL's hidden sequence. The radical problem is that MySQL can not insert and return the PK at the same time, but Hibernate need this while INSERTing a new Entity.
The Problems you run into:
If Hibernate save a new Entity, he try to immerdentelly set the id to the new EntityBean. Therefore hibernate must read what ID will the Database use before hibernate save the new Tuple to the Table.
If you have multiple Servers who access the database, you shall let hibernate's session-factory decide to use the built-in sequence(AUTO-INCREMENT) or let hibernate decide (GenerationType.AUTO/GenerationType.IDENTITY) how large the open range of reserved PK's is (Job of a DB-Architect). (We have about 20 servers to one Database, so on a good-used table we use a PK-distance of +100). If only one server have access to the database GenerationType.TABLE shall be correct.
Hibernate must calculate the next id by yourself using max(*)+1 but:
What if two requests ask for max(*)+1 at the same time/with the same result? Right: The last try to insert will fail.
So you need to have a Table LAST_IDS in the database who stores the last Table-PK's. If you like to add one, you must do this steps:
Start read-optimistic transaction.
SELECT MAX(address_id) FROM LAST_IDS
store the maximum in a java-variable i.e.: $OldID.
$NewID = $OldID + 1. (+100 in pessimistic-lock)
UPDATE LAST_IDS SET address_id= $newID WHERE address_id= $oldID?
commit the read-optimistic transaction.
if commit was successfull, store $newID to setID() in the HibernateBean you like to save.
Finally let Hibernate call the insert.
This is the only way i know.
BTW: Hibernate-Entitys shall only use inheritance if the Database support inheritance between tables like PostgreSQL or Oracle.
Because you use the TABLE identifier generator you need to have that table created. If you are not using the enhanced identifier generators, chances are you are going to use the MultipleHiLoPerTableGenerator.
The MultipleHiLoPerTableGenerator can use one table for all table identifier generators.
My suggestion is to grab the table ddl from your integration tests, in case you use hbmddl to build the test schema. If you use flyway or liquibase for testing, you can add a maven plugin to generate the ddl schema.
Once you have the schema, you need to take the exact create table command and make add it to your MySQL database.

Why does nullable = false force a FK to be put in on insert, but without it it does an update to set the FK in JPA/Hibernate

Using JPA annotations and hibernate we ran in to an issue recently with a unidirectional onetomany mapping that looked like this:
#Entity
public class FooOrder extends AbstractEntity{
#OneToMany(cascade= CascadeType.ALL,orphanRemoval = true)
#JoinColumn(name = "fooOrderId")
private List<FooItem> fooItems = new ArrayList<FooItem>();
public void addFooItem(foo item properties here)
{
fooItems.add(fooItem);
}
}
#Entity
public class FooItem extends AbstractEntity {
SomeRandomStuffButNoLinkToParent
}
The test code was basically this:
FooOrder fooOrder = new FooOrder(stuff here);
fooOrder.addFooItem(foo item properties here);
fooOrder = fooOrderRepository.save(fooOrder);
When we run tests on this we get sql that looks something like this:
insert FooOrder(columns here)
insert FooItem(columns here missing the FK to FooOrder)
update FooItem set FooOrderFK to proper key.
but if I set #JoinColumn(name = "activeOrderId", nullable = false) then my sql looks something like this:
insert FooOrder(columns here)
insert FooItem(columns here with FK to FooOrder)
Why does hibernate set the FK through an update if it's nullable, but set it in the insert when it's not nullable?
Well when the foreign key is not null able
insert FooOrder(columns here)
insert FooItem(columns here with FK to FooOrder)
Is the only way to actually execute the inserts. So the real question is why this is not always done this way.
My take on this is that the way using updates works in times when the other would not ever work.
Let assume we have some kind of circular foreingn key relationship.
A has a foreign key to B
B has a foreign key to A
if none of this foreign keys is nullable there might be no way to insert this.
You can not insert A first because the foreign key to B can not be null.
Same goes for B.
Also note that my example might be over simplyfied. This circular foreign key can be caused by any number of tables.
So first inserting the data and then adding the foreign keys is just the way that works in this more complicated scenario. Your not null constraint forces Hibernate to take another way. For the default case first inserting then adding the keys is just he better idear.

Hibernate mapping a specific field to be loaded by a table join

Is there a way I can map a field in an hibernate object to be loaded with a table query?
As an example lets say Table_Message has fields id(int),message_key(varchar),message_content(Clob),language(varchar). This table will hold messages in different languages(locale).
And another table thats mapped to an entity using hibernate. Comments with fields id(int),comment_message_id(varchar),created_date(datetime). comment_message_id refers to Table_Message's message_key column.
EDIT: Table_Message is NOT a mapped Entity in hibernate
Assuming my comment class is
public class Comment
{
int id;
String message;
Date createdDate;
}
Is there a way to tell hibernate to load message by joining Comment table and Table_Message table by message_key with a default locale (for example 'en').
Basically is there a way to tell hibernate to load a field by running a specific query? And if so what is that way?
I know about writing a Custom SQL query for loading the entity. But since I'm using XDoclet there doesn't seem to be a way to do that. Also it will be very convenient if there's a way to do that for a single field.
I guess ResultTransformer may help you in this. Please check
http://docs.jboss.org/hibernate/orm/3.3/api/org/hibernate/transform/ResultTransformer.html
http://stackoverflow.com/questions/6423948/resulttransformer-in-hibernate-return-null
You must join the tables by comment_message_id with message_key and further filter the result by language. I assume the message_key is unique.
As a side notice: you should use integer keys to have better performance.
You can try to write a database view in SQL and create an entity to opaque the view:
CREATE VIEW Comment_Table_Message AS
SELECT c.id, c.comment_message_id, c.created_date, m.id AS mid,
m.message_content, m.language
FROM Comment c, Table_Message m
WHERE c.comment_message_id = t.message_key;
Now you can create an entity CommentTableMessage and use JPQL to filter results by language:
SELECT x FROM CommentTableMessage x WHERE x.language=?1
If Table_Message was a Hibernate entity you would write (in JPA terms):
#Entity
public class Comment
{
int id;
#ManyToOne()
#JoinColumn(name="comment_message_id")
TableMessage tableMessage;
String message;
Date createdDate;
}
#Entity
public class TableMessage {
int id;
#Id
String messageKey;
bytes[] messageContent; //I don't know how you want to deal with Blobs?
String language;
}
Having that you can write a simple JPA Query: (Can you use JPA ? - next assumption)
SELECT c FROM Comment c WHERE c.tableMessage.language=?1

Does JPA support mapping to sql views?

I'm currently using Eclipselink, but I know now days most JPA implementations have been pretty standardized. Is there a native way to map a JPA entity to a view? I am not looking to insert/update, but the question is really how to handle the #Id annotation. Every entity in the JPA world must have an ID field, but many of the views I have created do not conform to this. Is there native support for this in the JPA or do I need to use hacks to get it to work? I've searched a lot and found very little information about doing this.
While using the #Id annotation with fields of directly supported types is not the only way to specify an entity's identity (see #IdClass with multiple #Id annotations or #EmbeddedId with #Embedded), the JPA specification requires a primary key for each entity.
That said, you don't need entities to use JPA with database views. As mapping to a view is no different from mapping to a table from an SQL perspective, you could still use native queries (createNativeQuery on EntityManager) to retrieve scalar values instead.
I've been looking into this myself, and I've found a hack that I'm not 100% certain works but that looks promising.
In my case, I have a FK column in the view that can effectively function as a PK -- any given instance of that foreign object can only occur once in the view. I defined two objects off of that one field: one is designated the ID and represents the raw value of the field, and the other is designated read-only and represents the object being referred to.
#Id
#Column(name = "foreignid", unique = true, nullable = false)
public Long getForeignId() {
...
#OneToOne
#JoinColumn(name = "foreignid", insertable=false, updatable=false)
public ForeignObject getForeignObject() {
...
Like I said, I'm not 100% sure on this one (and I'll just delete this answer if it turns out not to work), but it got my code past a particular crash point.
Dunno if it applies to your specific situation, though. And there's an excellent chance that after 11 months, you no longer care. :-) What the hell, that "Necromancer" badge doesn't just earn itself....
In my view I have a "unique" id, so I mapped it as the Entity id.
It works very well:
#Entity
#Table(name="table")
#NamedQuery(name="Table.findAll", query="SELECT n FROM Table n")
public class Table implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="column_a")
private int columnA;
JPA - 2.5.4
CREATE MATERIALIZED VIEW IF NOT EXISTS needed_article as select product_id, count(product_id) as count from product_article group by product_id;
CREATE MATERIALIZED VIEW IF NOT EXISTS available_article as select product_id, count(product_id) as count from article a inner join product_article p
on a.id = p.article_id and a.stock >= p.amount_of group by product_id;
CREATE UNIQUE INDEX productId_available_article ON available_article (product_Id);
CREATE UNIQUE INDEX productId_needed_article ON needed_article (product_Id);
Entity.java
#Entity
#Immutable // hibernate import
#Getter
#Setter
public class NeededArticle {
#Id
Integer productId;
Integer count;
}
Repository.java
#Repository
public interface AvailableProductRepository extends CrudRepository<AvailableArticle, Integer> {
#Query("select available.productId from AvailableArticle available, NeededArticle needed where available.productId = needed.productId and available.count = needed.count")
List<Integer> availableProduct();

Categories