I have such a case where I need to have internally many-to-one using hibernate proxies and only id externally, here using MapsId. The issue appears when I try to save something, because the target entity is not fetched, when I set the value only on the id.
Let's take an example: I have an Account table and DeviceConfig table. Inside the DeviceConfig's class definition, I add account in a many-to-one relation and accountId in relation with #MapsId.
Now when creating, I always set a value to accountId, but never the value is picked up, and the backend throws an SQL error, because the field cannot be null.
#Table(name = "djl_device_config")
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
public class DeviceConfig extends CoreEntity {
...
#JsonIgnore
#ManyToOne
#MapsId("accountId")
#JoinColumn(name = "account_id")
private Account account;
#Column(name = "account_id", insertable = false, updatable = true, nullable = true)
private UUID accountId;
}
So I suppose this is a config error on my side, but I've been reading the JPA for these three days and I still don't know what's wrong or what I should do to achieve the behaviour I expect.
That for any help you'll provide.
Related
I'm using Hibernate Envers for Auditing Change Data, I have a Class that store information about companies like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Compnay {
private String name;
private String code;
}
and it's using Envers for keeping the changes of companies.
also, I have a class for Keep the data of items that manufacture in any of this company, the class will be like this :
#Getter
#Setter
#Entity
#Table(name = "COMPNAY")
#Audited
public class Item {
#Column(name = "NAME", nullable = false)
private String name ;
#Column(name = "CODE", nullable = false)
private String code;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COMPANY_ID", nullable = false)
private Compnay compnay;
}
Consider that there is a company in company table like this :
ID
NAME
CODE
1
Apple
100
2
IBM
200
and the data in the item's table will be like this :
ID
NAME
CODE
COMPANY_ID
3
iPhone
300
1
4
iPad
400
1
if I edit the information of Apple company and change the code from 100 to 300 how can I fetch the information of Items that were saved before this change with the previous code? Is there is any way to reference to audit table?
Yes, you can write a HQL query that refers to the audited entities. Usually, the audited entities are named like the original ones, with the suffix _AUD i.e. you could write a query similar to the following:
select c, i
from Company_AUD c
left join Item_AUD i on i.id.revision < c.id.revision
where c.originalId = :companyId
i'm using Spring Boot 2.4.2 and Data module for JPA implementation.
Now, i'm using an Oracle View, mapped by this JPA Entity:
#Entity
#Immutable
#Table(name = "ORDER_EXPORT_V")
#ToString
#Data
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class OrderExportView implements Serializable {
private static final long serialVersionUID = -4417678438840201704L;
#Id
#Column(name = "ID", nullable = false)
#EqualsAndHashCode.Include
private Long id;
....
The view uses an UNION which allows me to obtain two different attributes of the same parent entity, so for one same parent entity (A) with this UNION I get the attribute B in row 1 and attribute C in row 2: this means that the rows will be different from each other.
If I run the query with an Oracle client, I get the result set I expect: same parent entity with 2 different rows containing the different attributes.
Now the issue: when I run the query with Spring Data (JPA), I get the wrong result set: two lines but duplicate.
In debug, I check the query that perform Spring Data and it's correct; if I run the same query, the result set is correct, but from Java/Spring Data not. Why??
Thanks for your support!
I got it! I was wrong in the ID field.
The two rows have the same parent id, which is not good for JPA, which instead expects a unique value for each line.
So, now I introduced a UUID field into the view:
sys_guid() AS uuid
and in JPA Entity:
#Id
#Column(name = "UUID", nullable = false)
#EqualsAndHashCode.Include
private UUID uuid;
#Column(name = "ID")
private Long id;
and now everything works fine, as the new field has a unique value for each row.
My User class looks like this :
#Data
#Entity
public class User {
#Id
Long userID;
#ManyToMany(mappedBy = "admins")
private List<ClassRoom> classRooms = new ArrayList<>();
}
And my ClassRoom class like this :
#Data
#Entity
public class ClassRoom {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long classRoomID;
#ManyToMany
#JoinTable(name ="classroom_user",
joinColumns = #JoinColumn(name = "classroom_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> admins = new ArrayList<>();
}
And in my UserController class, I have :
#PostMapping("user/{id}/c")
User addClassRoom(#PathVariable Long id,#RequestBody ClassRoom newClassRoom)
{
logger.debug(repository.findById(id));
return repository.findById(id)
.map(user -> {
user.getClassRooms().add(newClassRoom);
user.setClassRooms(user.getClassRooms());
return repository.save(user);
})
.orElseGet(() -> {
return null;
});
}
And I POST and empty JSON ({}) and I see no change in my users. The Classroom or an empty Classroom doesn't get added in the User.
What is the problem here? How can I resolve this ?
user.getClassRooms().add(newClassRoom); is suffice, user.setClassRooms(user.getClassRooms()); not required.
You will have to perform cascade save operation.List all cascade types explicitly and don't use mappedBy, instead use joincolumns annotation.
Can you paste the logs, please? Is Hibernate doing any insert into your table? Has the database schema been created in the DB correctly? One thing I recommend you to do is to add a custom table name on the top of your User class, using annotations like so: #Table(name = "users"). In most SQL dialects user is a reserved keyword, hence it is recommended to always annotate User class a bit differently, so that Hibernate won't have any problems to create a table for that entity.
IMO you must find classRoom by its id from repository, if it's new, you must create a new entity and save it first. Then assign it to user and save it.
The object you receive from the post method was not created by the entity manager.
After using user.getClassRooms().add(newClassRoom);
We must use userRepository.save(user);
I'm using hibernate in my spring boot application
my domain model is like this
#Entity
#Table(name = "skill")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "skill")
#Audited
public class Skill implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
}
The increment size of sequence is 50 and is working properly
but when I add Envers Audited annotation I see this error
conflicting values for 'increment size'. Found [50] and [1]
How can I resolve this conflict?
This doesn't sound like an Envers problem but a general mapping problem.
When you add an #Audited annotation, that simply informs Envers that it should inspect that particular entity mapping during Hibernate bootstrap and create the necessary audit objects to store the entity state during each transaction.
The generated Envers objects use their own sequence generators and primary key. The user defined generation strategy, sequences, etc are all ignored in the Envers object because the associated column is meant to just be a copy/pass-thru value, nothing special.
In other words, the Envers table would have a PK defined that mirrors this POJO:
#Embeddable
public class EnversSkillId implements Serializable {
#Column(name = "REV", nullable = false, updatable = false)
private Integer rev;
#Column(name = "id", nullable = false, updatable = false)
private Long id;
}
When Envers generates the audit record, it automatically uses its internal sequence generator to get the next value and assign it to EnversSkillId#rev and copies your entity's id value directly into the EnversSkillId#id property.
So as mentioned in the comments, your problem is very unlikely related to Envers.
Yet another thread like this. I fighting with this for 4 days.
annotation #Getter and #Setter are from lombok plugin
My Place class
#Entity
public class Place {
#Getter
#Setter
#OneToMany(mappedBy = "place", targetEntity = Tag.class, cascade = CascadeType.ALL)
private Set<Tag> tags;
//...
}
Tag class which should be many
#Entity
public class Tag {
#Getter
#Setter
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_place_id")
private Place place;
//...
}
I'm saving it like this
Tag tagOne = new Tag("tagOne");
Tag tagTwo = new Tag("tagTwo");
Set<Tag> tagSet = new HashSet<>();
tagSet.add(tagOne);
tagSet.add(tagTwo);
Place place = new Place();
place.setTags(tagSet);
placeService.save(place);
saving looks is the single line sessionFactory.getCurrentSession().persist(entity) on every case. Saving entity with #OneToOne mapping it works like a charm.
You have a bidirectional association. You're initializing only one side of the association, and that side is the inverse side (because it has the mappedBy attribute). Hibernate only cares about the owner side. So, for Hibernate, there is no association between the tags and the place.
Note that cascade=ALL on a ManyToXxx association doesn't make sense. If 100 tags are referencing the same place, and you delete one of these tags, you don't want to also delete the place. And even if you want to, that won't work, because 99 other tags still reference it, which will cause a referential integrity error.