public class ValidateClaimDataEntity{
...
#OneToMany(mappedBy = "claimDataEntity")
private List<ValidateEventDataEntity> eventDataEntityList;
}
when I do
function(ValidateClaimDataEntity claimDataEntity){
claimDataEntity
.getEventDataEntityList().parallelStream()....
}
I got null point exception, and I debug got claimDataEntity
.getEventDataEntityList() is null
But actually this claimDataEntity have related event data in db
and I do it in UT like :
claimDataEntityRepository.findById(32L).get().getEventDataEntityList().parallelStream().forEach(eventDataEntity -> {
log.info(eventDataEntity.getValidateEventDataId());
});
It do log event data
So, why the function claimData get eventList as null???
--------------------v1----------------------------------------
I found it might not be the problem of stream,
Actually I do a save before the iterator
public ValidateClaimResponse bc(ValidateClaimRequest claimRequest) {
//claim
ValidateClaimDataEntity claimDataEntity = new ValidateClaimDataEntity(claimRequest);
claimDataEntityRepository.save(claimDataEntity);
claimRequest.getEventRequestList()
.forEach(eventRequest -> {
...
//event
ValidateEventDataEntity eventDataEntity = new ValidateEventDataEntity(eventRequest);
eventDataEntity.setValidateClaimDataId(claimDataEntity.getValidateClaimDataId());
eventDataEntityRepository.save(eventDataEntity);
});
System.out.println(claimDataEntity.getEventDataEntityList() == null ? "null" : claimDataEntity.getEventDataEntityList() );
ValidateClaimDataEntity claimDataEntity2 = claimDataEntityRepository.findById(claimDataEntity.getValidateClaimDataId()).get();
System.out.println(claimDataEntity2.getEventDataEntityList() == null ? "null2" : claimDataEntity2.getEventDataEntityList());
And I got both null of eventList
Default fetchtype for #OneToMany is LAZY(Default fetch type for one-to-one, many-to-one and one-to-many in Hibernate). Hence it is not fetched. Make it EAGER
#OneToMany(mappedBy = "claimDataEntity", fetch = FetchType.EAGER)
private List<ValidateEventDataEntity> eventDataEntityList;
Related
I once again joined a project which uses Hibernate (Spring/Hibernate/Kotlin to be exact) and have read through a number of Vlad Mihalcea wonderful articles to refresh my knowledges about this ORM (this article is of my current interest).
What I'm trying to understand is how should I treat add/update/delete operations for nested entities (bidirectional #OneToMany). Here is what I don't understand.
Say we have a Post entity:
#Entity(name = "Post")
#Table(name = "post")
class Post(
#Id
#GeneratedValue
var id: Long? = null,
val title: String,
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private val comments: MutableList<PostComment> = MutableList<PostComment>()
) {
fun addComment(comment: PostComment) {
comments.add(comment)
comment.post = this
}
fun removeComment(comment: PostComment) {
comments.remove(comment)
comment.post = null
}
}
And a PostComment entity:
#Entity(name = "PostComment")
#Table(name = "post_comment")
class PostComment(
#Id
#GeneratedValue
var id: Long? = null,
val review: String,
#ManyToOne(fetch = FetchType.LAZY)
var post: Post
) {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is PostComment) false
return id != null && id == o.id
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
}
All in all everything is good, but here is the couple of things I don't know how to cover:
In fact Post class won't compile since I set post field of PostComment to null while it is not nullable. What is a good practice to handle it? Should I make all relations nullable in kotlin just because hibernate require it to be so and it is in contradiction with business logic?
It is more or less clear how to add and delete nested entities, though what should we do if we need to update already existing nested entity. Let's imagine we have a Post(id=1, title="lovely post", comments=[PostComment(id=15, review="good", post=this)] and we get a update action with the following PostDto(id=1, title="not that nice post", comments=[PostComment(id=15, review="bad", post=this)]. As you can see we need to update title for Post and review for PostComment. If we take a look at the Vlad's article I linked above we do not see any update methods. I think it was just ommited since it is not related to article topic.
But I wonder what is the good practice to handle such an update? Something like these two approaches comes to my mind but I'm not sure if these are the best things to do:
#Entity(name = "Post")
#Table(name = "post")
class Post(
//fields...
) {
fun addComment(comment: PostComment) {
comments.add(comment)
comment.post = this
}
fun removeComment(comment: PostComment) {
comments.remove(comment)
comment.post = null
}
// not effective, since issue delete/insert queries, but clean
fun updateComment(comment: PostComment) {
val commentId = comment.id!!
comments.removeIf { it.id == commentId }
comment.team = this
}
// effective, since issue only update query, but dirty as hell
fun updateComment(commentId: Long, review: String) {
val comment = comments.find { it.id == commentId }!!
comment.review = review
}
}
Not actual anymore. Refer good explanation by #Chris in a comment section.
Imagine we need an endpoint to update a comment only. What
is the best way to organise our code base for such a scenario?
Should we always update it like this (always fetch old post, looks
inefficient) or is there any better/efficient approach?
#Transactional
fun reassignComment(newPostId: Long, commentDto: CommentDto) {
val comment = commentRepo.findByIdOrNull(commentDto.id)!!
val oldPost = comment.post
val newPost = postRepo.findByIdOrNull(newPostId)!!
oldPost.removeComment(comment)
newPost.addComment(comment)
}
Thanks anyone for your time and input!
I have a parent which stores a list of children. When i update the children(add/edit/remove), is there a way to automatically decide which child to remove or edit based on the foreign key? Or do i have to manually check through all the child to see which are new or modified?
Parent Class
#Entity
#EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {
#OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "permit")
private List<Coordinate> coordinates;
}
Child Class
#Entity
public class Coordinate extends Identifiable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "permit_id", referencedColumnName = "id")
private Permit permit;
private double lat;
private double lon;
}
Parent's Controller
#PutMapping("")
public #ResponseBody ResponseEntity<?> update(#RequestBody Permit permit) {
logger.debug("update() with body {} of id {}", permit, permit.getId());
if (!repository.findById(permit.getId()).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}
=EDIT=
Controller Create
#Override
#PostMapping("")
public #ResponseBody ResponseEntity<?> create(#RequestBody Permit permit) {
logger.debug("create() with body {}", permit);
if (permit == null || permit.getId() != null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
List<Coordinate> coordinates = permit.getCoordinates();
if (coordinates != null) {
for (int x = 0; x < coordinates.size(); ++x) {
Coordinate coordinate = coordinates.get(x);
coordinate.setPermit(permit);
}
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}
Controller Update
#PutMapping("")
public #ResponseBody ResponseEntity<?> update(#RequestBody Permit permit) {
logger.debug("update() with body {} of id {}", permit, permit.getId());
if (!repository.findById(permit.getId()).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
List<Coordinate> repoCoordinate = coordinateRepository.findByPermitId(permit.getId());
List<Long> coordinateIds = new ArrayList<Long>();
for (Coordinate coordinate : permit.getCoordinates()) {
coordinate.setPermit(permit);
//if existing coordinate, save the ID in coordinateIds
if (coordinate.getId() != null) {
coordinateIds.add(coordinate.getId());
}
}
//loop through coordinate in repository to find which coordinate to remove
for (Coordinate coordinate : repoCoordinate) {
if (!(coordinateIds.contains(coordinate.getId()))) {
coordinateRepository.deleteById(coordinate.getId());
}
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}
I have tested this and it is working, is there no simplified way of doing this?
You were close to the solution. The only thing you're missing is orphanRemoval=true on your one to many mapping:
#Entity
#EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {
#OneToMany(mappedBy = "permit", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Coordinate> coordinates;
}
Flagging the mapping for orphan removal will tell the underlying ORM to delete any entities that no longer belong to any parent entity. Since you removed a child element from the list, it will be deleted when you save the parent element.
Creating new elements and updating old is based on the CascadeType. Since you have CascadeType.ALL all elements in the list without an ID will be saved to the database and assigned a new ID when you save the parent entity, and all elements that are already in the list and have an ID will be updated.
On a side note, you might need to update the setter method for List coordinates to look something like:
public void setCoordinates(List<Coordinates> coordinates) {
this.coordinates = coordinates;
this.coordinates.forEach(coordinate -> coordinates.setPermit(this));
}
Or simply use #JsonManagedReference and #JsonBackReference if you're working with JSON.
I have a parent which stores a list of children.
Lets write the DDL for it.
TABLE parent (
id integer pk
)
TABLE child(
id integer pk
parent_id integer FOREIGN KEY (parent.id)
)
When i update the children(add/edit/remove), is there a way to automatically decide which child to remove or edit based on the foreign key?
Assuming you have a new child #5 bound to the parent #2 and:
The FK in the DDL is correctly
The entitys knows the FK
You are using the same jpa-context
The transaction is executed correctly
Then every call to parent.getChilds() must(!) return all the entitys that are existing before your transaction has been executed and the same instance of the entity that you have just committed to the database.
Then, if you remove child #5 of parent #2 and the transaction executed successfully parent.getChilds() must return all entitys without child #5.
Special case:
If you remove parent #2 and you have cascade-delete in the DDL as well as in the Java-Code all childrens must be removed from the Database as well as the parent #2 in the Database you just removed. In this case the parent #2 is not bound anymore to the jpa-context and all the childrens of parent #2 are not bound anymore to the jpa-context.
=Edit=
You could use merge. This will work for constructs like this:
POST {
"coordinates": [{
"lat":"51.33",
"lon":"22.44"
},{
"lat":"50.22",
"lon":"22.33"
}]
}
It will create one row in table "permit" and two rows in table "coordinate", both coordinates are bound to the permit-row. The result will include the ids set.
But: You will have to do the validation work (check that id is null, check that coordinates not refering different permit, ...)!
The removal of coordinates must be done using the DELETE method:
DELETE /permit/972/coordinate/3826648305
I have a case statement in my Native query where I am attempting to override a field in my entity.
SELECT i.id, i.ONE_TO_ONE_ID, i.ANOTHER, CASE(WHEN condition THEN 'YES' WHEN another_condition THEN 'NO' ELSE 'MAYBE' END) as word ....
I am using this with JpaRepository as a native query, with pagination.
When I run the native query against my db directly, the result set looks as though I expect.
| id_value | MAPPED_ENTITY_ID_value | another value | word_value (YES) |
When I run the native query from my JpaRepository, everything works there, except word is always null. I cant' seem to figure out how to map the additional String word result to a field in my Entity.
Is there a way to get this to map? Or will I have to create an entire #SqlResultSetMapping() for all of my fields coupled with a native query? (hoping not)
UPDATE: 1
I was generalizing above. Here is my Query.
#Query(
name = "listPagedMapping",
value = "SELECT DISTINCT i.ID, i.INSTANCE_ID, i.REGION, i.CNAME_STACK_ID, i.INSTANCE_STATE, i.IP_ADDRESS, i.EC2_ROLE_NAME, i.INSTANCE_OWNER, i.IS_MASTER, i.EC2_MASTER_ID, i.CNAME, i.EC2_START_TIMESTAMP, i.PRIVATE_DNS, i.INSTANCE_NAME, i.AUTO_TERMINATE, i.AUTO_TERMINATE_DATE, i.TERMINATION_ZONE, i.ADMIN_GROUP_AD_LDAP_ID, i.USER_GROUP_AD_LDAP_ID, (CASE WHEN i.INSTANCE_OWNER=:username THEN 'OWNER' WHEN i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) THEN 'ADMIN' WHEN i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) THEN 'USER' END) as PERMISSION FROM USER u, USER_ACCESS_GROUPS g, EC2_PROVISIONING i WHERE i.INSTANCE_OWNER=:username and i.INSTANCE_STATE in (:instanceStates) or u.username=:username and i.INSTANCE_STATE in (:instanceStates) and g.USER_ID=u.USER_ID and (i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) or i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID))",
countQuery = "SELECT count(*) FROM (SELECT DISTINCT i.* FROM USER u, USER_ACCESS_GROUPS g, EC2_PROVISIONING i WHERE i.INSTANCE_OWNER=:username and i.INSTANCE_STATE in (:instanceStates) or u.username=:username and i.INSTANCE_STATE in (:instanceStates) and g.USER_ID=u.USER_ID and (i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) or i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID))) as ug",
nativeQuery = true)
Page<Ec2Instance> findAllByPermissionUserAdminOrOwnerAndInstanceStateIn(
#Param("username")final String username,
#Param("instanceStates") final Set<String> instanceStates,
final Pageable pageable);
}
Obviously a bit more complex.
I can get it to map to the entity field with using a named query, but then I loose all the default mappings:
#JsonInclude(JsonInclude.Include.NON_NULL)
#SuppressWarnings("unused")
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(exclude={"masterNode", "workers", "associatedBuckets"})
#Entity
#Table(name = "EC2_PROVISIONING")
#SqlResultSetMapping(
name="listPagedMapping",
columns = {
#ColumnResult(name = "permission", type = String.class)
}
)
#NamedNativeQuery(
name = "listAccessibleInstances",
query = ACCESSIBLE_QUERY,
resultSetMapping = "listPagedMapping"
)
public class Ec2Instance {
....
private String permission;
#column(name = "INSTANCE_ID")
private String instanceId;
#ManyToOne
#JoinColumn(name = "EC2_MASTER_ID")
private Ec2Instance masterNode;
#Setter(AccessLevel.NONE)
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "WORKER_EC2_NODES", joinColumns = { #JoinColumn(name = "EC2_MASTER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "ID") })
private Set<Ec2Instance> workers = new HashSet<>();
... More fields ..
}
I guess, I am hoping there is a way to provide a single mapping on-top of the default mapping that is done by ORM. The above code results in only a pageable of Content PERMISSION, rather than the whole entity + permission.
UPDATE: 2
Ok, so I am getting closer... Seems by removing the #ColumnResult I do get the default mapping, plus the PERMISSION field mapped over! Looks like this:
#SqlResultSetMapping(
name="listPagedMapping"
)
The last issue is it does not accept my CountQuery, and causes my tests to fail whenever a Pagination Query results with multiple pages. Looks like Spring try's to come up with its own CountQuery, which is not correct.
UPDATE: 3
To finish this off, looks like I can provide the Count Query as described here: Spring Data - Why it's not possible to have paging with native query
I will give this a go and update back.
I never got this to work quite how I wanted. I am sure I could by mapping my entire entity, but, that would have been painstaking. I ended up solving this by using NamedNativeQueries, with mapping for the additional Column as a result of my Case statement. My entity class is now annotated like:
#JsonInclude(JsonInclude.Include.NON_NULL)
#SuppressWarnings("unused")
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(callSuper = false)
#Entity
#Table(name = "EC2_PROVISIONING")
#SqlResultSetMappings({
#SqlResultSetMapping(
name = "listPagedMapping",
entities = {
#EntityResult(
entityClass = Ec2Instance.class
)
},
columns = {#ColumnResult(name = "permission", type = String.class)}
),
#SqlResultSetMapping(name = "listPagedMapping.count", columns = #ColumnResult(name = "cnt"))
})
#NamedNativeQueries({
#NamedNativeQuery(
name = "Ec2Instance.listAccessibleInstances",
query = ACCESSIBLE_QUERY,
resultClass = Ec2Instance.class,
resultSetMapping = "listPagedMapping"
),
#NamedNativeQuery(
name = "Ec2Instance.listAccessibleInstances.count",
resultSetMapping = "listPagedMapping.count",
query = ACCESSIBLE_QUERY_COUNT
)
})
We also dont need the permission field in this entity anymore. I removed that.
Then in my Repository:
Page<Object[]> listAccessibleInstances(
#Param("username")final String username,
#Param("instanceStates") final Set<String> instanceStates,
final Pageable pageable);
Thats it! Now the result of my case statement is returned with each entity.
Object[0] = original, default mapped entity.
Object[1] = permission
I'm using Nhibernate to fetch a collection which has lazy loaded properties but am having trouble returning it as the Serializer tries to serialize the lazy property after the Nhibernate Session is closed. So is there a way to tell NHibernate to give me a true list in which if there were unloaded lazy collections that it would just leave them empty?
For example
IEnumerable<Store> stores = StoreService.GetList(1, 2);
Store has a one-to-many mapping with StockItems which is set to lazy load which then causes the serialization error. I tried
List<Store> stores_r = stores.ToList();
but I get the same thing. Is there something that will traverses through the list and fetches one-to-one relations and ignores one-to-many lazy loading and return a finished list?
Thanks
EDIT:Solution I've tried but still not working
public class NHibernateContractResolver: DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType) || typeof(NHibernate.Proxy.ILazyInitializer).IsAssignableFrom(objectType))
{
var oType = objectType.GetInterfaces().FirstOrDefault(i => i.FullName.StartsWith("Navace.Models"));
return oType != null ? base.CreateContract(oType) : base.CreateContract(objectType.BaseType);
}
return base.CreateContract(objectType);
}
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
{
return base.GetSerializableMembers(objectType.BaseType);
}
else
{
return base.GetSerializableMembers(objectType);
}
}
}
Try to manually serialize so I can use what's happening
IEnumerable<Store> stores = StoreService.GetList(1, 2);
Store> storess = stores.ToList();
JsonSerializer sr = new JsonSerializer
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
};
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);
sr.Serialize(jsonWriter, storess);
string res = stringWriter.ToString();
The error I get is
Outer exception : Error getting value from 'datedcost' on 'PartProxy'.
Inner exception: No row with the given identifier exists[Navace.Models.Part#0]
My recommendation is to return view models instead of domain models. It's confusing to return an empty collection property when it may have data. By converting the domain model to a view model (using LINQ Select or AutoMapper), the serializer will only touch (and attempt to lazy load) the properties in the view model.
I´ve got this on the parent object
#OneToMany(mappedBy="idUser", cascade = CascadeType.MERGE)
public List<Directions> directions;
And in my controller I´ve got this
public static void userUpdate(String apikey, JsonObject body) {
if(validate(apikey)) {
Long idUser = decode(apikey);
User oldUser = User.findById(idUser);
Map<String, User> userMap = new HashMap<String, User>();
Type arrayListType = new TypeToken<Map<String, User>>(){}.getType();
userMap = gson().fromJson(body, arrayListType);
User user = userMap.get("user");
oldUser.em().merge(user);
oldUser.save();
}else{
forbidden();
}
}
It makes the update on the parent object but when I change something on the children object it doesn't update it and neither gives problems with hibernate or Oracle.
Does anyone know why it doesn´t update the child object?
Thanks all!
Updated with solution!
This is how it works for me, as #JB Nizet said you´ve got to save the child objects too.
oldUser.em().merge(user);
oldUser.save();
for (Direction direction : oldUser.directions) {
direction.save();
}
Another aproach!
With this
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "SASHNBR", insertable = true, updatable = true)
public List<Direction> directions;
I´ve been able to make oldUser.save() and get the child objects saved.
AFAIK, Play requires a call to save() on all the modified entities. So you probably need to iterate through the user's directions and save them as well:
for (Direction direction : user.getDirections()) {
direction.save();
}