Can the following id generation be implemented without depending on Hibernate #GenericGenerator, with pure JPA only?
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
#Entity
public class MyObject implements Serializable {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "my.IdGenerator")
#Column(name = "ID", length = 36, nullable = false)
private String id;
// ...
}
You can use a #PrePersist callback
#Id
#Column(name = "ID", length = 36, nullable = false)
private String id;
// ...
#PrePersist
public void prePersist() {
id = UUID.randomUUID();
}
More on Life Cycle callbacks here: Configuring a Life Cycle Callback Method on a JPA Entity
There are a few JPA methods for doing this (meaning it could be implemented by EclipseLink, Hibernate, or somet other JPA implemntation)
Here's how you'd do it if you had a sequence object:
#Id
#javax.persistence.SequenceGenerator( name = "mySequence", sequenceName = "MY_TABLE_SEQ", allocationSize = 1, initialValue = 1 )
#javax.persistence.GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "mySequence" )
#Column( name = "MY_TABLE_ID" )
private Integer myTableId;
And here's how if it were an identity column:
#Id
#javax.persistence.GeneratedValue( strategy = GenerationType.IDENTITY )
#Column( name = "MY_TABLE_SEQ" )
private Long myTableId;
Again, the thing to note here is that this is pure JPA, not Hibernate-specific.
Related
I am new to Spring and JPA and I am trying to write a job in Spring which runs every 3 hours and retrieve the records from Oracle Database.
I would like to only read the new/updated content from the past 3 hours (ideally from the last job run).
I have seen few examples in https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/ where we can create queries and retrieve the data based on our requirements, but in my current use case, I am not using queries instead using the java classes with the annotations and using Join columns between different tables. There are chances that only one of the sub table is updated or all the tables are updated with new content. I need to get the results if at least one of the table is updated/inserted with new content.
Campus is the main table and retrieves the data from Group and Region, I need to fetch the data if any new data is updated in Campus table or even any group/region is added/modified.
I am using JDK7 as of now.
Is there a way to accomplish the above requirement?
Below are the sample Java classes for reference.
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_CAMPUS")
public class Campus implements Auditable {
#Id
#Column(name = "ID)
#SequenceGenerator(name = "SIMPLE_ID", sequenceName = "SIMPLE_ID")
#GeneratedValue(generator = "SIMPLE_ID", strategy = GenerationType.AUTO)
private Long id;
#Column(name = "CAMPUS_NAME")
private String campusName;
#Column(name = "CAMPUS_ID", nullable = false)
private Long campusId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GROUP_ID")
private GroupType groupType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "REGION_ID")
private Region region;
....
...
}
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_GROUP_TYPE")
public class GroupType implements Auditable {
#Id
#Column(name = "GROUP_TYPE_ID")
#SequenceGenerator(name = "GROUP_TYPE_SEQUENCE", sequenceName = "GROUP_TYPE_ID")
#GeneratedValue(generator = "GROUP_TYPE_SEQUENCE", strategy = GenerationType.AUTO)
protected Long id;
#Column(name = "GROUP_TYPE_NAME", nullable = false)
protected String groupTypeName;
....
...
}
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_REGION")
public class Region implements Auditable {
#Id
#Column(name = "region_id")
#SequenceGenerator(name = "REGION_ID", sequenceName = "REGION_ID")
#GeneratedValue(generator = "REGION_ID", strategy = GenerationType.AUTO)
private Long id;
#Column(name = "REGION_NAME", nullable = false)
private String name;
...
..
}
Any help is Appreciated.
This field "auftragsnummer" always stays null, even though it is annotated with #GeneratedValue:
#Entity
public class Auftrag implements Serializable
{
#Id
#GeneratedValue
private int id;
#Pattern(regexp = AUFTRAGSNUMMER_REGEXP, message = "{validator.auftragsnummer}")
#Length(min = 20, max = 20)
#GenericGenerator(name = "sequence_auftragsnummer", strategy = "de.software.AuftragsnummerGenerator")
#GeneratedValue(generator = "sequence_auftragsnummer")
#Column(unique = true, nullable = false)
private String auftragsnummer;
}
I store it using getHibernateTemplate().persist(t). The referenced generator class implements org.hibernate.id.IdentifierGenerator.
I have no idea why it is ignored.
Environment:
Hibernate 5.0.1
Spring 4.2.1
Java 7
DB2 10
JPA only mandates support for #GeneratedValue on #Id fields.
If your order number doesn't depend on the entity's primary key, then the easiest solution is to use #PrePersist e.g.
#PrePersist
public void onCreate() {
auftragsnummer = ...;
}
When JPA tries to select AdmUser entity I have sql error:
ERROR: column locations1_.name does not exist.
Is there anything wrong with my entities? My AdmUser entity:
#Entity
#Table(name = "ADM_USERS")
#SequenceGenerator(name = "ADM_USER_SEQ", sequenceName = "ADM_USER_SEQ", allocationSize = 1)
public class AdmUser implements EntityInt, Serializable {
private static final long serialVersionUID = 786L;
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ADM_USER_SEQ")
private Long id;
(...)
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinTable(name = "loc_locations_adm_users", joinColumns = #JoinColumn(name = "id_user", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name = "id_location"))
#OrderBy("name")
private Set<LocLocation> locations;
(...)
}
My LocLocation Entity:
#Entity
#Table(name = "loc_locations", schema = "public")
#SequenceGenerator(name = "LOC_LOCATIONS_SEQ", sequenceName = "LOC_LOCATIONS_SEQ", allocationSize = 1)
public class LocLocation implements EntityInt, java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "LOC_LOCATIONS_SEQ")
private Long id;
#Column(nullable = false, unique = true, length = 200)
private String name;
(...)
#ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy="locations")
private List<AdmUser> users;
}
And now - when JPA tries to select AdmUser entity I have sql error. The query generated by JPA looks that:
select
admuser0_.id as id1_2_0_,
admuser0_.actived as actived2_2_0_,
admuser0_.admin as admin3_2_0_,
admuser0_.allow_ip as allow_ip4_2_0_,
admuser0_.created as created5_2_0_,
admuser0_.deleted as deleted6_2_0_,
admuser0_.id_domain as id_doma16_2_0_,
admuser0_.email as email7_2_0_,
admuser0_.language as language8_2_0_,
admuser0_.login as login9_2_0_,
admuser0_.name as name10_2_0_,
admuser0_.passwd as passwd11_2_0_,
admuser0_.phone as phone12_2_0_,
admuser0_.picture as picture13_2_0_,
admuser0_.surname as surname14_2_0_,
admuser0_.theme as theme15_2_0_,
locations1_.id_user as id_user1_2_1_,
loclocatio2_.id as id_locat2_6_1_,
loclocatio2_.id as id1_17_2_,
loclocatio2_.description as descript2_17_2_,
loclocatio2_.name as name3_17_2_
from
public.ADM_USERS admuser0_
left outer join
public.loc_locations_adm_users locations1_
on admuser0_.id=locations1_.id_user
left outer join
public.loc_locations loclocatio2_
on locations1_.id_location=loclocatio2_.id
where
admuser0_.id=1
order by
locations1_.name
The order by points to locations1_.name, but should be loclocatio2_.name. Have I anything wrong with my entities?
You have a Set on one side for that field. Consequently there is no "ordering" (other than what hashCode() gves). Use a List if you want ordering (this is Java, nothing to do with JPA really).
You also seem to be missing a "mappedBy" on the non-owner side of that M-N.
The #OrderBy works fine with ManyToMany. Also with the structure I provided in my question. The problem was my query and JPA didn't managed with it. Sorry.
This are my entities:
public class Account extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID", nullable = false)
private Long id;
...
}
public class Integration extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName="SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID", nullable = false)
private Long id;
...
public void addIntegration(Integration integration) {
IntegrationAccount association = new IntegrationAccount();
// This does not help
//association.setIntAccountsPK(new IntAccountsPK(integration.getId(), this.getId()));
association.setAccount(this);
association.setIntegration(integration);
this.integrationAccounts.add(association);
integration.getIntAccountsCollection().add(association);
}
}
And this is entity for join table
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount {
#EmbeddedId
protected IntAccountsPK intAccountsPK;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
#Embeddable
public class IntAccountsPK implements Serializable {
#Column(name = "INT_ID", nullable = false)
private Long intId;
#Column(name = "ACC_ID", nullable = false)
private Long accId;
...
}
And when i do:
account.addIntegrations(integrations.getTarget());
account.setCustomer(customer);
accountService.save(account);
I got this in my log
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.dhl.dcc.domain.IntegrationAccount
I dont have many knowledge about this kind of mapping, can you please tell me how to improve this mapping (entity for join table have to be preserved) and how to save account with related integrations? Thanks.
I know this question has already been marked as solved but I disagree with the accepted answer. This answer modifies the datamodel by adding a useless column (the new id) in the table INT_ACCOUNTS. There is another way to solve this problem in Hibernate without modifying the datamodel :
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "INT_ID_FK")
private Integration integration;
#Id
#ManyToOne
#JoinColumn(name = "ACC_ID_FK")
private Account account;
}
#Entity
#Table(name = "INTEGRATIONS")
public class Integration {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName = "SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID")
private Long id;
}
#Entity
#Table(name = "ACCOUNTS")
public class Account {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID")
private Long id;
}
You could create a ID field for your IntegrationAccount and then create a unique constraint for your two fields.
#Entity
#Table(name = "INT_ACCOUNTS",
uniqueConstraints=#UniqueConstraint(columnNames={"ACC_ID", "INT_ID"}))
public class IntegrationAccount {
#Id
private Long id;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
Works like a charm!
I'm using my uuid as following:
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
#Column(name = "uuid", unique = true)
private String uuid;
but I'm getting a smart Hibernate warning:
Using
org.hibernate.id.UUIDHexGenerator
which does not generate IETF RFC 4122
compliant UUID values; consider using
org.hibernate.id.UUIDGenerator instead
So I want to switch to org.hibernate.id.UUIDGenerator, now my question is how should I tell it to Hibernate's generator. I saw some guy used it as a "hibernate-uuid" - so this is what I've tried, but with negative result:
#Id
#GeneratedValue(generator = "hibernate-uuid")
#GenericGenerator(name = "hibernate-uuid", strategy = "hibernate-uuid")
#Column(name = "uuid", unique = true)
private String uuid;
It should be uuid2:
...
#GenericGenerator(name = "uuid", strategy = "uuid2")
...
See 5.1.2.2.1. Various additional generators.
HibernateDoc says you can use following:
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
#Column(name = "uuid", unique = true)
private String uuid;
I hope you are using Hibernate 3.5.
As #natan pointed out in a comment, if you are using Hibernate 5 the below code is sufficient:
#Id
#GeneratedValue
private java.util.UUID id;
Define the id column with the type of BINARY(16) in MySQL or it's equivalent in other SQL implementations.
Try...
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "uuid", columnDefinition = "BINARY(16)")
public UUID getId()
{
return id;
}
public void setId(UUID i)
{
id = i;
}
Note the "uuid2" as opposed to "uuid".
This will use UUID v4 and the auto generated uuid will be stored in the column as usual varchar(36):
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(length = 36)
private String uuid;
This should have some performance impact:
consumed size is more than BINARY(16)
after hydration the java.lang.String instance consumes more memory than java.util.UUID: 112 bytes for UUID as string versus 32 bytes (i.e. two longs + obj header) for UUID.
But it's much more easier to work with string'ed UUID - easier to write queries and you can see the contents of the table.
Tested on Hibernate 5.3
Unknown Id.generator: hibernate-uuid
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", unique = true)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
With current 5.4.2 Hibernate version,
if you want a Human-Readable varchar(36) field in the database table,
but also a Serializable UUID data type in your Java Class,
you can use #Type(type = "uuid-char") at the same time you declare your field member with java.util.UUID type.
Note that #Column(length = 36) is important to reduce from 255 to 36 the field length in MySQL.
Note that with PostgreSQL you should use #Type(type = "pg-uuid") instead.
import org.hibernate.annotations.Type
import java.util.UUID
import javax.persistence.Column
import javax.persistence.GeneratedValue
import javax.persistence.Id
#Id #GeneratedValue
#Type(type = "uuid-char") #Column(length = 36)
private UUID id;
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
#Column(name = "UUID_ID")
public String getId(){
return id;
}
This is the proper way to use annotation for uuid generators in Hibernate 5.0.11.FINAL.
Note: IT is deprecated.