I am using the Hibernate Tools ant task to generate DDL from JPA annotated entities. With hibernate annotations you can name the foreign key using
#JoinColumn(name = "foo")
#org.hibernate.annotations.ForeignKey(name = "fk_foo")
Is there a pure JPA way of achiving the same?
No. JDO is the only persistence specification allowing definition of FK names, onUpdate/onDelete actions etc. JPA (even in JPA2) simply doesn't go there.
--Andy (DataNucleus)
Not in annotation. You can however set the columnDefinition and write the foreign key in there.
Related
I am switching ORM framework from Hibernate to OpenJPA.
In Hibernate we could annotate a field with #ColumnTransformer like below.
#Column(name = "EMP_NAME", length = 4000)
#ColumnTransformer(
read = "pgp_pub_decrypt(emp_name::bytea,dearmor('"+key1+"'))",
write = "pgp_pub_encrypt(?, dearmor('"+key2+"'))"
)
private String empName;
How to do the same in OpenJPA
I am not sure of OpenJPA specific capabilities related to this, but the following two alternatives would work for all JPA providers:
Create an updatable view that does the necessary transformations and map the entity to the view instead of the table.
Move the transformations to middleware and apply them in entity lifecycle callbacks.
The other benefit of both solutions is that you keep the entities clean of custom native SQL.
I'm trying to understand EclipseLink behaviour in case if I use native query. So I have Entity like this:
class Entity {
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="other_entity_id")
private OtherEntity otherEntity;
#Column(name = "name")
private String name;
//gets ... sets ...
}
and corresponding table looks like:
**ENTITY**
INTEGER ID;
VARCHAR NAME;
OTHER_ENTITY_ID;
And then I run native query
Query query = getEntityManager().runNativeQuery("select * from ENTITY", Entity.class);
query.getResultList()
Within Entity I have declared OtherEntity otherEntity which is annotated with FetchType.LAZY, however my query selects (*) - all of the columns, including OTHER_ENTITY_ID. The question is - if I run native query that fetches all columns, will fields annotated with FetchType.LAZY populated as if they were FetchType.EAGER or not? I've never worked with EclipseLink before and tyring to decide is it worth using it or not so I would really appreciate any help
Thanks, Cheers
My first advice is to turn on EclipseLink's SQL logging, and execute the equivalent JPQL to load what you are looking for and see the SQL EclipseLink generates to accomplish that to get an understanding of what is required to build objects in your native queries based on your current mappings.
Relationships generally loaded with a secondary query using the values read in from the foreign keys, so eager or lazy fetching is not affected by the native query to read in "Entity" - the query requires the other_entity_id value regardless of the fetch type. When required based on eager/lazy loading, EclipseLink will issue the query required by the mapping.
You can change this though by marking that the relationship is to use joining. In this case, EclipseLink will expect not only the Entity values to be in the query, but the referenced OtherEntity values as well.
When certain non key fields of a entity are generated in the database (for instance, by triggers) a call to persist will not bring back values that the database has just generated. In practice this means that you may need to refresh an entity after persist or merge (and when level 2 cache is enabled you may even need to evict the entity).
Hibernate have a custom annotation #Generated which handles Generated Properties.
// Refresh property 1 on insert and update
#Generated(GenerationTime.ALWAYS)
#Column(insertable = false, updatable = false)
private String property1;
// Refresh property 2 on insert
#Generated(GenerationTime.INSERT)
#Column(insertable = false)
private String property2;
JPA #GeneratedValue only works with primary key properties.
So, my question is if there is a replacement for #Generated on JPA API (maybe on 2.1)? And if there isn't one, what is the best practice to handle non key database generated fields?
I read the specs from the beginning until the end and it is not such thing, nothing comparable with #Generated, sorry , and as you said.
The GeneratedValue annotation may be applied to a primary key property
or field of an entity or mapped superclass in conjunction with the Id
annotation.
What you could do is use Event Listener #PrePersist and #PreUpdate to set some properties by default or generated by utility classes before em persist the object , try that approach it comes to my mind to something similiar.
In my scenario, I have a schema generation script to create tables and required indexes. I am wondering is there any need to define #Index annotation in hibernate entities as well, if so why?
Script:
create table issues (id, project_id, .., status_id)
create index idx_issues_projid on issues (project_id)
Entity:
#Table(name="issues")
public class DBIssue {
..
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PROJECT_ID")
#Index(name="INDEX_TFW_ISSUE_PROJECT_ID")
private DBProject project;
}
Hibernate configuration:
<property name="hibernate.hbm2ddl.auto">off</property>
I presume you're asking about the Hibernate #Index annotation, which has essentially been imported into JPA 2.1. You would use #Index anywhere you would otherwise proactively tell a relational database to index a column, primarily on a field where you know you'll be doing lots of lookups. In this case, for example, you are probably going to want to "select the DBIssues belonging to a particular DBProject frequently, so it would make sense to index that column in the table holding DBIssue.
Let's say we have an entity
#Entity
public class Person {
#Id int id;
#Basic String name;
#Basic String remark;
}
Let's say "remark" field is filled with big texts, but rarely used. So it would be good if when you run jpql: SELECT p FROM Person p, EclipseLink just executes sql select id, name from person
And than when you call person.getRemark(), it will get fetched with select remark from person where id = ?.
Is it possible with EclipseLink 2.1?
You can indeed define a fetch attribute in a Basic annotation and set it to LAZY. But let me quote what the specification says about it:
11.1.6 Basic Annotation
(...)
The EAGER strategy is a requirement
on the persistence provider runtime
that data must be eagerly fetched.
The LAZY strategy is a hint to the persistence provider runtime that
data should be fetched lazily when it
is first accessed. The implementation
is permitted to eagerly fetch data for
which the LAZY strategy hint has
been specified. In particular, lazy
fetching might only be available for
Basic mappings for which
property-based access is used.
In the particular case of EclipseLink, the behavior will depend on the context (Java EE vs Java SE) as explained in What You May Need to Know About EclipseLink JPA Lazy Loading.
In a Java EE environment (assuming the container implements the appropriate container contracts of the EJB 3.0 specification):
EclipseLink JPA performs lazy loading when the fetch attribute is set to javax.persistence.FetchType.LAZY.
In a Java SE environment:
By default, EclipseLink JPA ignores the
fetch attribute and default javax.persistence.FetchType.EAGER applies.
To configure EclipseLink JPA to perform lazy loading when the fetch attribute set to FetchType.LAZY, consider one of the following:
How to Configure Dynamic Weaving for JPA Entities Using the EclipseLink Agent
How to Configure Static Weaving for JPA Entities
Try add annotation #Basic(fetch = FetchType.LAZY)
#Entity
public class Person {
#Id int id;
#Basic String name;
#Basic(fetch = FetchType.LAZY) String remark;
}
We solved this problem (when using ActiveRecord and Hibernate) by putting the large string (usually a CLOB or BLOB) into it's own table with a FK to the main table (Person in this case.) Then it works like you want.