consider an entity with just an id and text field:
#lombok.Data
class Entity {
#javax.persistence.Id
UUID id;
String name;
}
consider that the table definition is as follows:
create table entity (
id uniqueidentifier not null primary key default newid(),
name varchar(max)
);
I am then curious why this doesn't work and how i could make it work:
UUID savedId = entityRepository.save(new Entity().setName("entity name")).getId();
In JPA, entity IDs can be either assigned by the application code, or generated (by the JPA provider, such as Hibernate, or by the database). In many situations, it's desirable to have the entity IDs be generated instead of applicaiton-assigned; it seems like that's what you are expecting.
If so, you need to annotate the id field with #GeneratedValue. For example:
class Entity {
#Id
#GeneratedValue
UUID id;
String name;
}
Note that there are considerations to be made regarding the generation strategy, so you'll want to educate yourself about them and make the right choice based on your situation. This is a good page that discusses the options. This SO Answer also is worth reading (the author is a well-known expert on JPA and Hibernate).
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.
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.
I am developing a Java Application that uses Hibernate and is connected to an Oracle instance. Another client is looking to use the same application, but requires it run on MS SQL Server. I would like to avoid making changes to the existing annotations and instead create a package of xml files that we can drop in depending on the environment.
One way to do this is using JPA XML configuration to override the existing class annotations. However, JPA does not support generic generators, which is a requirement due to the structure of our legacy database. The other way that I am looking into is to use Hibernate XML configs to remap entire classes and have access to the generator xml tag. This solution has some issues though:
Hibernate does not allow you to selectively override entity members
Hibernate does not allow you to re-map the same class (e.g. org.hibernate.AnnotationException: Use of the same entity name twice)
Does anyone have any experience with overriding annotations using Hibernate XML Configuration files or is JPA the only way to go?
Update with an Example
In Oracle, Sequences are used to generate unique IDs when inserting new records into the database. An id would then be annotated in the following manner:
#Id
#GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.SEQUENCE)
#SequenceGenerator(name="EXAMPLE_ID_GEN", sequenceName="SEQ_EXAMPLE_ID")
#Column(name = "EXAMPLE_ID")
public String getExampleId() {
return this.exampleId;
}
However, MS SQL Server does not have the concept of Sequences (Ideological differences). Therefore, you could use a table generator to simulate sequences.
#Id
#GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.TABLE)
#TableGenerator(name="EXAMPLE_ID_GEN", tableName="SEQUENCE", valueColumnName="VALUE", pkColumnName="SEQUENCE", pkColumnValue="EXAMPLE_ID")
public String getExampleId() {
return this.exampleId;
}
Two different configurations for two different types of databases. Keep in mind that this is a legacy database and that we aren't going to rewrite our application to support SQL Server identities, the native id generator for SQL Server (which would also require a different annotation).
To alleviate this, I have looked into using Hibernate's #GenericGenerator and point it to a class of my own creation that models org.hibernate.id.SequenceGenerator (or something similar) and also customize the structure of the table by extending org.hibernate.id.TableStructure.
Back to my original question - is any of this possible with an XML override?
How I Solved this Problem
So, in the end, I found that JPA and Hibernate did not provide the out-of-box functionality that I was looking for. Instead, I created a custom generator that checked the database dialect and set the TableStructure appropriately. As I explored all options, I ended up using Hibernate's #GenericGenerator annotation. This is an example of the Id generation annotation:
#Id
#GeneratedValue(generator="EXAMPLE_ID_GEN")
#GenericGenerator(name = "EXAMPLE_ID_GEN", strategy="com.my.package.CustomIdGenerator", parameters = {
#Parameter(name = "parameter_name", value="parameter_value")
})
public String getExampleId() {
return this.exampleId;
}
This solution necessitates that each Hibernate entity be modified with the new Id generator.
I think that if you don't use AnnotationConfiguration when configuring your SessionFactory, the annotations will be omitted.
So, use Configuration.
For your generator problem (for which the solution would normally be "use the native generator" but doesn't work for you due to working with a legacy db), you could probably extend the SQLServerDialect and override the getNativeIdentifierGeneratorClass to return a (possibly custom) generator that does what you need for your legacy db.
I have come across the need to mix-n-match legacy with new schemas/databases before in a Grails (GORM) application, which of course is running Hibernate 3 underneath.
Would not say "you're doing it wrong" - but I would keep the JPA #Annotations to the very basics like #Entity and #Column and leave it to the Hibernate dialect, which is also specified in the XML configuration file.
You might experiment with subclassing the Oracle10gDialect with one that assigns a sequence generator to all tables, versus a Sybase one which does not.
Please see this post on how to implement this.
UPDATE:
What james and I are suggesting (almost in the same minute) is to setup multiple persistence-unit sections of your persistence.xml file.
This allows one to use #Entity and #Id without supplying details in the class. The details come in the hibernate.dialect property. I suggested subclassing Oracle10gDialect (and james the SQLServerDialect) - those would do the choosing as to the table naming, id generator strategy, etc.
See --> https://forum.hibernate.org/viewtopic.php?f=1&t=993012
If you rewrite the annotations in HBM XML files, you could maintain two sets of such XML and pick which ones to use via Hibernate's mapping directives. I've done this in Hibernate Core, but not in a J2EE/JPA environment so I don't know if there are any gotchas in that respect.
The biggest downside is it likely will be a lot of work to remove all your annotations and rebuild them in XML.
I would say that if your annotations are database specific, you're doing it wrong.
In my case:
Rack and Slot are entities having custom ID Generators. I am using unidirectional one-to-one mapping. Dimension table will hold the data with a Autogenerated Custom ID as foreign key for multiple tables (Rack and Slot for example here).
And my schema looks like this : Rack ------> Dimension <-----------Slot
where Dimension will hold the data for Rack and Slot table with Generated ID.
Here the concern is that when i am saving the data like this:-
Rack rack = new Rack(params);
Dimension dim = new Dimension(params);
rack.setDimension(dim);
session.save(rack);
Data is being saved successfully with same Autogenerated ID in Rack and Dimension Tables.
But when I am saving the data for Slot table :
Slot Slot = new Slot(params);
Dimension dim = new Dimension(params);
slot.setDimension(dim);
session.save(slot);
it is showing error message as:-
attempted to assign id from null one-to-one property: rack
Can I pass the dynamic property name as "slot" when saving the data for Slot and Dimension and "rack" when saving the data for Rack and Dimension.
#GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
#Parameter(name = "property", value = "slot"),
#Parameter(name = "property", value = "rack")})
Rack.java
#Entity
#Table(name="tablename")
#GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
#Id
#GeneratedValue(generator = "customseq")
#Column(name = "uni_id")
private String id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Dimension dimension;
// Getters and Setters
}
Slot.java
#Entity
#Table(name="tablename")
#GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
#Id
#GeneratedValue(generator = "customseq")
#Column(name = "uni_id")
private String id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Dimension dimension;
// Getters and Setters
}
Dimension.java
public class Dimension implements Serializable{
#Id
#Column(name = "systemid")
#GeneratedValue(generator = "foreign")
#GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
#Parameter(name = "property", value = "slot"),
#Parameter(name = "property", value = "rack")})
private String systemid;
#OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private Rack rack;
#OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private Slot slot;
// Getters and Setters
}