idMoneda property not updated in DB with #Transient directive - java

I have this entity, in which I have made a PUT and POST method, which do not give an error but nevertheless the idMoneda, which is a property calculated with #Transient because it is the ID of the moneda(where there is a 1 to 1 relationship with another table), it does not update me, when I look at the database it remains null even though in the POST request I put a value. I don't know if it's because the setter is wrong, or just that something else needs to be added that I don't see right now.
#Entity
#Table(name = "REMESA")
public class Remesa {
#Id
#SequenceGenerator(name = "remesa_sequence", sequenceName = "remesa_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "remesa_sequence")
#Column(name = "ID")
private Long id;
#OneToOne
#JoinColumn(name = "moneda", nullable = true)
#JsonIgnore
private Moneda moneda;
#Transient
#JsonProperty("moneda")
private Long idMoneda;
public Long getIdMoneda() {
return this.moneda.getId();
}
public void setIdMoneda(Long idMoneda) {
this.idMoneda = idMoneda;
}
}

#Transient in JPA means: do not save this field in DB. A column named "moneda_id" will automatically be generated by your relationship if it's well-defined

Java's transient keyword is used to denote that a field is not to be serialized, whereas JPA's #Transient annotation is used to indicate that a field is not to be persisted in the database, i.e. their semantics are different. Because they have different meanings.
So try to remove the transient annotation and run your code .

Related

How to provide Initial value OR Increment ID with JPA GenerationType.AUTO

I am using following code to define MyEntity,
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
For the first POST after my application starts, I create MyEntity everything works fine, MY_TABLE_ID starts with 1 and works as expected.
My issue is, If somebody inserts data manually before I do my POST then I get duplicate key exception as myTableId is entered as 1 which is already present.
My main problem is I can't create database sequence for using GenerationType.SEQUENCE now to resolve this as database can't be altered now.
I have tried various combinations of GenerationType, TableGenerator but I am unable to successfully tackle it.
Setting initialValue to some larger number to avoid duplicate values can temporarily resolve my problem but I am unable to do it too.
If someone can help me with initialValue with AUTO or give me some other better solution without database changes will be great :)
As MY_TABLE_ID is an identity column, following annotations will work.
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY) // <-- IDENTITY instead of AUTO
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
The identity column will automatically assign an value as soon as the transaction is committed. You are not to set any values for an identity column, as its the job of the database to assign the values. Therefore you also don't need to think about any initial values (forget them completely for identity columns)
I tried various options in answers provided here and for similar questions on stackoverflow and other forums,
I had few limitations,
I couldn't create database sequence as my database changes were freezed.
I didn't want to introduce new Custom IdGenerator class because it would add confusion to other people working with me.
It was resolved using following change:
Adding GenericGenerator with increment strategy helped me, I made following changes to my code.
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator="seq")
#GenericGenerator(name = "seq", strategy="increment")
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
It helped me because,
From Hiberbate DOCs
increment
An IdentifierGenerator that returns a long, constructed by counting
from the maximum primary key value at startup. Not safe for use in a
cluster!
Since, it was incrementing already existing myTableId even if it was manually inserted, this resolved my issue.
You can also implement your own generator if you need more control.
See this interface IdentifierGenerator.
So you can get the count of records, for example through a #NamedQuery.
Then you can generate an identifier yourself.
public class MyEntityKeyGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SessionImplementor session, Object object) {
// SELECT count(ent) from MyEntity ent;
Long count = (Long) session.getNamedQuery("count-query").uniqueResult();
// calc and return id value
}
}
Entity:
class MyEntity {
#Id
#GenericGenerator(name = "my_generator",
strategy = "org.common.MyEntityKeyGenerator")
#GeneratedValue(generator = "my_generator")
private Long id;...
Just do not forget about the lock.
I use the generation type Identity, which basically means that the db, takes care of Id generation.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#MappedSuperclass
#EntityListeners(EntityListener.class)
#EqualsAndHashCode(of = {"id", "createdAt"})
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ID id;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED_AT", updatable = false)
private Date createdAt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "UPDATED_AT")
private Date updatedAt;
}
You can also use, Sequence generation:
#Entity
#SequenceGenerator(name="seq", initialValue=1, allocationSize=100)
public class EntityWithSequenceId {
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
#Id long id;
}

How to Audit two tables with one-to-one relation through hibernate envers?

So I have two entity classes, Subscription and MailDetail. There is a one-to-one relation between them.
Here are the classes -
Subscription.class-
#Data
#Entity
#AllArgsConstructor
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public class Subscription {
#Id
#GeneratedValue(strategy =`enter code here` GenerationType.SEQUENCE, generator = "subscription_subscription_id_seq")
#SequenceGenerator(initialValue = 1, allocationSize enter code here= 1, name = "subscription_subscription_id_seq", sequenceName = "subscription_subscription_id_seq")
#Column
private Long subscriptionId;
#Column
private String template;
#Column
private String fileFormat;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "mail_id", referencedColumnName = "mail_id")
private MailDetail mailDetail;
}
MailDetail.class -
#Data
#Entity
#Audited
public class MailDetail {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mail_detail_mail_id_seq")
#SequenceGenerator(initialValue = 1, allocationSize = 1, name = "mail_detail_mail_id_seq", sequenceName = "mail_detail_mail_id_seq")
#Column
private Long mailId;
#Column
private String emailId;
}
So this creates two audit tables, subscription_aud and mail_detail_aud.
Creation/Updates in mail_detail table is done through Subscription repository only. So my requirement is that whenever there is a change in mail_details table's fields, I need an audit entry in subscription_aud. Or basically i want change in mail_detail table be considered as a change in subscription table.
I am using hibernate envers for auditing. How can i achieve this?
Unfortunately, not with how Envers is currently designed.
Any to-many association that is either had its contents modified or elements in the collection changed will propagate an audit change for the owning side of the relationship by default; however that can even be disabled by setting org.hibernate.envers.revision_on_collection_change to false. When this configuration setting is disabled, only element-collections will propagate a change to the owner because elements in such a collection are not entities, therefore can only be queried through the owning entity.
For to-one associations, this is a completely different story.
The only way to accomplish what you want would be to introduce some column on your Subscription instance that owns the MailDetail and change the value of that column when this use case happens. Based on the fact you only modify MailDetail via the aggregate root Subscription, this should work however I agree that its less than ideal.

JPA - Check existence of value in another table as Boolean value

I have a (abbreviated) class that looks like this:
#Entity
#Table
#SecondaryTable(
name = "SUPER_ADMIN",
pkJoinColumns = #PrimaryKeyJoinColumn(
name = "PERSON_ID",
referencedColumnName = "PERSON_ID"))
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
// getters/setters omitted for brevity
}
The SUPER_ADMIN table has only one column: PERSON_ID. What I would like to do is add private Boolean superAdmin to Person where it would be true if the PERSON_ID is present in that table.
Is this even possible? I am using Hibernate as my JPA provider, so I'm open to proprietary solutions as well.
UPDATE
It seems like I should have done more homework. After poking around, I see that #SecondaryTable does inner joins and not outer joins. Therefore, my idea here will not work at all. Thanks to #Elbek for the answer -- it led me to this revelation.
You can use JPA callback methods.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
#Transient
private transient Boolean superAdmin = false;
// This method will be called automatically when object is loaded
#PostLoad
void onPostLoad() {
// BTW, personId has to be present in the table since it is id column. Do you want to check if it is 1?
superAdmin = personId == 1;
}
}
or you can create easy getter method.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
boolean isSuperAdmin() {
return personId == 1;
}
}
You can't have an optional relationship with a #SecondaryTable. You do not have any other choice than using a #OneToOne optional relationship in that case.

Hibernate sequence generator always 0

I'm trying to configure a mapped class to use a sequence I defined in a postgres db. The ids are always zero when I try to persist any entities, if I use select nextval('item_seq'), I'll get 1 (or the next val). I used intellij to regenerate the classes. The version of hibernate in this project is 3.6.0, if that might be part of my problem?
#Entity
#Table(name = "item")
public class Item {
private int itemid;
...
#Basic
#Id
#SequenceGenerator(name = "item_seq", sequenceName = "item_seq", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "item_seq")
#Column(name = "itemid", unique = true, nullable = false, insertable = true, updatable = true)
public int getItemid() {
return itemid;
}
...
}
Usage
Item item = new Item();
item.setCreated(new Date());
item.setVendorId(vendorId);
save(item); // essentially is getHibernateTemplate().save(Item.class.getName(), item);
-- EDIT --
I've been trying the suggestions below and everything seems to keep generating 0 as an id or throw the exception 'ids for this class must be manually assigned before calling save()'. This is where I am now, as a last ditch effort I tried moving the annotations to the variable declaration instead of the getter. Didn't help.
#Entity
#Table(name = "item")
public class Item {
#Id
//#SequenceGenerator(name = "item_seq", sequenceName = "item_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY) //, generator = "item_seq")
#Column(name = "itemid", unique = true, nullable = false) //, insertable = true, updatable = true)
private Long itemid;
...
public Long getItemid() {
return itemid;
}
}
This always works for me (Hibernate 4.x though):
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id makes the column a primary key (unique, no nulls), so you don't need the #Column(...) annotation. Is something setting your itemId somewhere? You can remove its setter if you have one (Hibernate doesn't require).
Try to provide database schema name for given table and sequence in you entity class.
Make sure your application user has privileges (grants) to the sequence.
-- update --
I think solution is here
I was having the same problem and here's what I did to solve the issue
I was using Hibernate tools to auto generate POJOs and all the annotations were being placed at the method level, however, Spring recommends (requires?) them at the field level. You can't just move the Id annotations to the field level either, because it's either one or the other (I got some other exceptions when I tried this). So I followed this answer to customize Hibernate tools to generate POJOs with the annotations all at the field level.
Now my code looks like this and it's using the database sequence sequence just fine.
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="my_seq")
#SequenceGenerator(name="my_seq",sequenceName="SPT_PROJECT_SEQ", allocationSize=1)
#Column(name = "PROJECT_ID", unique = true, nullable = false, precision = 10, scale = 0)
private long projectId;
Hope this helps
what database u are using?
can be possibly a database that does not support sequence??
try to use strategy=auto and see how does it work
since 'select nextval(...)' works, your database (postgresql) is supporting sequence. so thats not it
maybe for some reason hibernate is threating yout int == 0 as an id and it is trying to update it instead of inserting a new record ( deafult value for int =0 ) just change your id type to Integer and see if it solve the problem

How to properly implement optimistic locking for related entities in JPA

Below is the simplified version of my data model:
#Entity
#Table(name = "DATA_TABLE")
public class DataTable implements Serializable {
#Id
#Column(name = "DATA_TABLE_ID")
private int dataTableId;
#Column(name = "TITLE")
private String title;
#OneToMany(mappedBy = "dataTableId", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Row> rows;
#Version
#Column(name = "VERSION")
private long version;
}
#Entity
#Table(name = "ROW")
public class Row implements Serializable {
#Id
#Column(name = "ROW_ID")
private int rowId;
#ManyToOne
#JoinColumn(name = "DATA_TABLE_ID")
private DataTable dataTable;
#OneToMany(mappedBy = "dataTableId", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Row> rows;
}
#Entity
#Table(name = "CELL")
public class Cell implements Serializable {
#Id
#Column(name = "CELL_ID")
private int cellId;
#ManyToOne
#JoinColumn(name = "ROW_ID")
private Row row;
#Column(name = "TEXT")
private String text;
}
I need to implement the optimistic locking for that model that is compatible with existing system.
In it, the version from the DataTable is sent to the user of the service, they have to send it back alongside the acion they performed and this is used to verify that there were no changes in the meantime. The version field is updated whenever there is a change to Row or Cell that belongs to that particular DataTable.
Unfortunately this does not seem to work with JPA #Version annotation(Hibernate 4.1.9). The version field is updated when there is a change to the properties persisted in DATA_TABLE table, e.g. title, but changing/adding rows and cells does not increment the version value.
Is there any way to achieve this functionality in JPA either by #Version or some other mechanism(expicit locking?) that prevents concurrency bugs such as Time of check to time of use?
I think it's because the field you're mapping to is a primitive type (long). You should use the Wrapper type Long instead.
JPA maintains a version number for every entity object. The initial version of a new entity object (when it is stored in the database for the first time) is 1. In every transaction in which an entity object is modified its version number is automatically increased by one. Version numbers are managed internally but can be exposed by defining a version field.
During commit (and flush), JPA checks every database object that has to be updated or deleted, and compares the version number of that object in the database to the version number of the in-memory object being updated. The transaction fails and an OptimisticLockException is thrown if the version numbers do not match, indicating that the object has been modified by another user (using another EntityManager) since it was retrieved by the current updater.
This behaviour happen only if the row is changed .. the foreign keys don't matter therefore the version number doesn't increase.

Categories