everyone I have a requirement that I want to generate two auto-generated values for two different columns. I am using Azure SQL DB as my RDBMS.
and I am using spring data JPA to persist my values.
Example:
#Entity
#Table(name="T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;
private String userRegistrationId;
public TJustForTest() {}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
}
here, I would like to use Id as my primary key as auto-generated and userRegistration Id also auto-generated but not primary key just a unique key and some custom format
Example
I will append some string as prefix and id as a suffix, meaning I will use the same primary key to generate the userRegistration No.
is there any way to achieve this or any other way around it, please clarify.
finally, I found a better solution to generate userRegistration by using Id,
I need to write one Listener class to get the auto-generated Id in my using #PostPersist
annotation, actually this will be called once the entity object persists in DB.
public class TJustForTestListener {
#PostPersist
public void getPostPersist(TJustForTest ob) {
try {
ob.setuserRegistrationId("CR"+ob.getId());
}catch(Exception e) {
e.printStackTrace();
}
}
and in Entity level i need to declare my listener class by using #EntityListeners
#Entity
#Table(name="T_JUST_FOR_TEST")
#EntityListeners(TJustForTestListener .class)
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;
private String userRegistrationId;
public TJustForTest() {}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
}
that's all it is required, JPA will insert the record first then update automatically.
Related
I am stuck with this error message, that appears every time I want to add a ManytoOne relationship with another entity class.
The class must use a consistent access type (either field or property). There is no ID defined for this entity hierarchy
This is my entity Transaction
#Entity
#Table(name = "CustomerTransaction")
public class CustomerTransaction implements Serializable {//this is the line with the error message
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne //This generates the problem
#JoinColumns({
#JoinColumn(name = "CUS_ID", referencedColumnName = "IDCUSTOMER") })
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private long transactionID;
#Temporal(TemporalType.TIMESTAMP)
private Date buyDate;
public Date getBuyDate() {
return buyDate;
}
public void setBuyDate(Date buyDate) {
this.buyDate = buyDate;
}
public long getTransactionID() {
return transactionID;
}
public void setTransactionID(long transactionID) {
this.transactionID = transactionID;
}
public String getCarYear() {
return carYear;
}
public void setCarYear(String carYear) {
this.carYear = carYear;
}
public Date getTransactionDate() {
return transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
private String carYear;
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate;
JPA annotation should all be placed either on fields or on accessor methods. You've placed the #Id and #GeneratedValue annotation on a field (private Long id), but #ManyToOne and #JoinColumns on a getter (public Long getId()). Move the latter on a field as well.
i had similar error but in the end, i realized #Id was referencing this package org.springframework.data.annotation.Id instead of javax.persistence.Id. i was using #MappedSuperClass approach so as soon as i corrected this, everything worked fine
You need to import #Id from "import javax.persistence.Id;"
I've setup JPA auditing with Spring Data JPA AuditingEntityListener and AuditorAware bean. What I want is to be able to persist auditor details even on entities with predefined identifiers.
The problem is that when JPA entity with predefined id is being persisted and flushed it's auditor details cannot be persisted:
object references an unsaved transient instance - save the transient instance before flushing: me.auditing.dao.AuditorDetails
The interesting part is that when an entity with a generated id is saved - everything's fine. In both cases the entities are new. I could not pinpoint the problem digging through hibernate code so I've created a sample project to demonstrate this (test class me.auditing.dao.AuditedEntityIntegrationTest) It has both entities with predefined and generated identifiers and should be audited.
The entities are:
#Entity
public class AuditedEntityWithPredefinedId extends AuditableEntity {
#Id
private String id;
public String getId() {
return id;
}
public AuditedEntityWithPredefinedId setId(String id) {
this.id = id;
return this;
}
}
and:
#Entity
public class AuditedEntityWithGeneratedId extends AuditableEntity {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
public String getId() {
return id;
}
public AuditedEntityWithGeneratedId setId(String id) {
this.id = id;
return this;
}
}
where parent class is:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = -7541732975935355789L;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#CreatedBy
private AuditorDetails createdBy;
#CreatedDate
private LocalDateTime createdDate;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#LastModifiedBy
private AuditorDetails modifiedBy;
#LastModifiedDate
private LocalDateTime modifiedDate;
And the auditor getter implementation is:
#Override
public AuditorDetails getCurrentAuditor() {
return new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
}
Edit 2016-08-08: It seems that when a new entity with predefined id is saved, it gets two different instances of createdBy and modifiedBy AuditorDetails, which is quite logical if the entity wouldn't be actually new. So, a completely new entity with generated gets both AuditorDetails of same instance, and the one with manually set id doesn't. I tested it by saving auditor details in AuditorAware bean before returning it to AuditingHandler.
Ok, so for now the only solution I could find is to actually persist AuditorDetails before writing it to audited entities like so:
#Override
#Transactional
public AuditorDetails getCurrentAuditor() {
AuditorDetails details = new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
return auditorDetailsRepository.save(details);
}
It is not the most elegant solution, but it works for now.
I am trying to establish unidirectional relationship between two entities called Person and Address,while saving Person(containing collection of Address) getting org.hibernate.TransientObjectException: object references an unsaved transient instance.
When I change cascadeType=all,child objects are getting propagated.But the problem here is with cascadeType=all Hibernate also tries to delete child entities on Deleting Owning entity.I don't want that to happen because in ManyToMany relationship child entity might be being referenced by some other entity.Ideally cascadeType=persist should do the job but unfortunately that give me mentioned exception.
Can somebody help me out how can I save the child objects (Address) with cascadeType=persist.I just wonder why cascadeType=persist is not doing the task of persisting.
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
#LazyCollection(LazyCollectionOption.FALSE)
#ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
private Collection<Address> address=new HashSet<Address>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Address> getAddress() {
return address;
}
public void setAddress(Collection<Address> address) {
this.address = address;
}
}
#Entity(name="Address")
public class Address {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="country")
private String country;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
In case of many- many relationship you always have to make use of JOIN Table(which is a third table).In that case you dont have a problem of Child entities getting deleted on deleting the Owning entity even if you use CASCADE-ALL.
Please see following post below:
Modeling many-to-many Relationship in JPA/Hibernate
I'll try to formulate the question more simple:
#Entity
public class One implements Serializable {
...
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
...
First I populate the List with some Many-Entities and persist the One-Entity. Second I retrieve (em.find) the One-Entity expecting the List in ascending order by Many#name, but it's not ordered by name. The List is ordered by id. Complete code see below if necessary.
Original post some days ago:
I'm using a current Netbeans Glassfish bundle.
Product Version: NetBeans IDE 8.0 (Build 201403101706)
Updates: NetBeans IDE is updated to version NetBeans 8.0 Patch 2
Java: 1.7.0_51; Java HotSpot(TM) 64-Bit Server VM 24.51-b03
Runtime: Java(TM) SE Runtime Environment 1.7.0_51-b13
System: Mac OS X version 10.9.3 running on x86_64; UTF-8; de_DE (nb)
The JPA #OrderBy annotation is completely ignored.
#Entity
public class One implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Many> getMany() {
return many;
}
public void setMany(List<Many> many) {
this.many = many;
}
}
The many Entity
#Entity
public class Many implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String name;
public Many() {
}
public Many(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The service class (EJB)
#Stateless
public class Service {
#PersistenceContext(unitName = "cwPU")
private EntityManager em;
public One createOne() {
return em.merge(new One());
}
public Many createMany(String name) {
return em.merge(new Many(name));
}
public One add(Long oneId, Long manyId) {
One one = em.find(One.class, oneId);
Many many = em.find(Many.class, manyId);
one.getMany().add(many);
return one;
}
public One find(Long id) {
One one = em.find(One.class, id);
return one;
}
}
The main class
public class Main {
public static void main(String[] args) throws NamingException {
EJBContainer container = EJBContainer.createEJBContainer();
Context ctx = container.getContext();
Service service = (Service) ctx.lookup("java:global/classes/Service");
One one = service.createOne();
Many many = service.createMany("the-first");
service.add(one.getId(), many.getId());
many = service.createMany("a-second");
one = service.add(one.getId(), many.getId());
one = service.find(one.getId());
System.out.println("-------------------------------------------------");
for (Many m : one.getMany()) {
System.out.println(m.getName());
}
container.close();
}
}
The output:
the-first
a-second
No matter what I write to the #OrderBy annotation (name ASC, name DESC, id ASC, id DESC), the output is always the same ascending order by the id.
Any idea what I'm missing?
The #Orderby annotation doesn't actually work that way. According to the javadoc, the annotation "Specifies the ordering of the elements of a collection ...at the point when the collection is retrieved."
So the annotation affects the result of the query (find), but does not dictate the order in the collection you store the result set into.
The solution is calling em.refresh (at the right place) as stated from Chris and WPrecht. I had to do this in a separate EJB method.
This did not work:
public One find(Long id) {
em.refresh(em.find(One.class, id)); // did not work
One one = em.find(One.class, id);
return one;
}
Adding a separate refresh method
public void refresh(Long id) {
em.refresh(em.find(One.class, id));
}
and calling it in the main program
...
service.refresh(one.getId());
one = service.find(one.getId());
...
works!
Probably I have to do more reading to understand caching.
Here is a composite primary key for one of my entities.
public class GroupMembershipPK implements Serializable{
private static final long serialVersionUID = 7816433876820743311L;
private User user;
private Group group;
public GroupMembershipPK(){
}
public boolean equals(Object o){
if(o==null){
return false;
}
if(o instanceof GroupMembershipPK){
final GroupMembershipPK groupMembershipPK=(GroupMembershipPK)o;
return groupMembershipPK.group.getGroupName().equals(this.group.getGroupName()) &&
groupMembershipPK.user.getName().equals(this.user.getName());
}
return false;
}
public int hashCode(){
return super.hashCode();
}
}
Heres my entity(part of) using the above as a Composite Primary Key.
#Entity
#IdClass(GroupMembershipPK.class)
public class GroupMembership extends AbstractModelElementVersionOther{
private static final long serialVersionUID = 9188465566607060376L;
private String memType;
private Group group;
private User user;
public GroupMembership(){
super();
}
#Column(nullable=false)
public String getMemType(){
return this.memType;
}
public void setMemType(String memType){
this.memType=memType;
}
#Id
#ManyToOne
#JoinColumn(name="groupId")
public Group getGroup(){
return this.group;
}
public void setGroup(Group group){
this.group=group;
}
#Id
#ManyToOne
#JoinColumn(name="userId")
public User getUser(){
return this.user;
}
public void setUser(User user){
this.user=user;
}
#Override
public boolean equals(Object o) {
//
I am a little confused on what should be the equals method implementation for the above entity. How can I compare two composite primary keys?
Any comments on other parts of my code are welcome too.
It is not a good idea you store entities as primary key. There is some limitations when using query language and JPA 1.0 does not support. Besides it there is no need to use entities as primary key. Think about it.If you want, take a special look at the following question
A class that behaves like #Entity and #Embeddable
Answer one
Comment about answer one
You will see that using a entity as primary key is not necessary.
Instead of
public class GroupMembershipPK implements Serializable {
private User user;
private Group group;
}
Use
public class GroupMembershipPK implements Serializable {
private Integer userId;
private Integer groupId;
}
equals implementation is important because JPA compares two entities by using it (JPA checks whether an entity is in persistence context by using equals implementation). So you can implement according to
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof GroupMembershipPK))
return false;
GroupMembershipPK other = (GroupMembershipPK) o;
if(!(getUserId().equals(other.getUserId()))
return false;
if(!(getGroupId().equals(other.getGroupId()))
return false;
return true;
}
Advice: it is a good idea you use property access instead of field access because, at some times, JPA implementation uses a proxy object because of performance issues. A proxy object makes use of property access because it allows JPA implementation hits the database when necessary.
How to save an object that uses a composite primary key ?
User user = new user();
Group group = new Group();
entityManager.save(user);
entityManager.save(group);
entityManager.flush();
UserGroup userGroup = new UserGroup();
userGroup.setId(new UserGroup.UserGroupId(user.getId(), group.getId()));
entityManager.save(userGroup);
Do you want to know how to implement UserGroup ?
public class UserGroup {
private UserGroupId id;
// You can create UserGroupId outside UserGroup class
// Feel free to choice your best approach
#Embeddable
public static class UserGroupId implements Serializable {
private Integer userId;
private Integer groupId;
// required no-arg constructor
public UserGroupId() {}
public UserGroupId(Integer userId, Integer groupId) {
this.userId = userId;
this.roupId = groupId;
}
// getter's and setter's
// equals and hashcode as shown above
}
#EmbeddedId
public UserGroupId getId() {
return this.id;
}
public setId(UserGroupId id) {
this.id = id;
}
}
Another approach to use composite primary key is IdClass. See IdClass
regards,
It was mentioned partially, anyway:
When implementing the equals method, you should use instanceof to allow comparing with subclasses. If Hibernate lazy loads a one to one or many to one relation, you will have a proxy for the class instead of the plain class. A proxy is a subclass. Comparing the class names would fail.
More technically, you should follow the Liskovs Substitution Principle and ignore symmetry.
The next pitfall is using something like name.equals(that.name) instead of name.equals(that.getName()). The first will fail, if that is a proxy.
http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html