Removing Many to one relationship in JPA using merge() - java

I have the below unidirectional Many To One mapping
#Entity
public class Item implements Serializable {
private Integer id;
private Double amount;
private Country origin;
#ManyToOne(optional=true)
#JoinColumn
public Country getOrigin() {
return this.origin;
}
}
#Entity
public class Country implements Serializable{
private String code;
private String desc;
}
Let say the relationship is optional so I am trying to remove the relation by updating it to null using code below
Country country = null;
//item is detached
item.setOrigin(country);
em.merge(item);
But the result turns out to be relationship is not removed.
However, this code works fine if country is not null and the system can update the relationship in DB.
It just simply ignore the field if it's null.
Can someone points out what setting can be changed in order to achieve my desired result?
P.S. Please be reminded that I am not wanting to delete the entity Country, but just remove the relationship between them.

Thanks all it's a mistaken question. It actually works.
There's just some client side issue submitting wrong data to it.

Related

Spring Data Neo4J reference related nodes without overwriting them on save

I'm struggling to write this, so I may have to give an example to help explain the problem I'm experiencing.
Say we have nodes of three types (these nodes may have more relationships of their own, e.g. Product Family, has product manager):
Product
Product Family
Battery
With these relationships
A product can be be in 0 or more families
A product can have 0 or more batteries.
When using spring-data-neo4j and saving a new Product, I wish to include these relatiopnships, such as the batteries they require and the product family they belong to. However if I only supply say an ID rather then a fully populated object, it overwrites this object along with properties and relations accordingly.
This isn't great as it means that I have to end up sending a fully populated object, with all it's relations everytime I wish to save something, and some of these relations may go quite deep.
My domain is as follows:
#Node
public class Product {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long productId;
private String name;
#Relationship(type = "REQUIRES_BATTERY", direction = OUTGOING)
private List<Battery> batteryList;
#Relationship(type = "IN_FAMILY", direction = OUTGOING)
private List<ProductFamily> productFamilyList;
}
#Node
public class Battery {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long batteryId;
private String name;
}
#Node
public class ProductFamily {
#Id
#GeneratedValue(generatorClass = SnowflakeGenerator.class)
private Long familyId;
private String name;
}
This could very well by from coming from a Relational Database mindset and is a 'limitation' of using Neo4J.
TLDR When persisting somethign in Neo4J using spring-data how can I save just a relationship, rather than a whole related Node.
You can make use of projections in Spring Data Neo4j. (https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#projections)
This gives you the option to put a "mask" on the object tree, you want to persist (and what should stay untouched).
For example in your case:
interface ProductProjection {
// without defining e.g. String getName() here, SDN would not ever touch this property.
List<BatteryProjection> getBatteryList();
List<ProductFamilyProjection> getProductFamilyList();
}
interface BatteryProjection {
String getName();
}
interface ProductFamilyProjection {
String getName();
}

Elasticsearch: search for two different document in a single query

I have two entities Merchant and Customer:
public class Merchant{
private UUID id;
private String name;
//... other fields and getters/setters
}
public class Customer{
private UUID id;
private String name;
//... other fields and getters/setters
}
These two entities are sightly different from each-other.
What I'am trying to to do is when I search with the term "John" I want to get both a merchant named "John Market" and a customer called "John Smith".
To achieve this I indexed these entities to a single index.
#Document(indexName = "merchant_customer_index", type = "merchantorcustomer")
public class MerchantOrCustomer {
#Id
private UUID id;
private String name;
private int type;
//...
My query can return both Merchant and Customer:
List<MerchantOrCustomer> result = elasticsearchTemplate.queryForList(nativeSearchQuery, MerchantOrCustomer.class);
I distinguish them programmatic(if(result.get(i).getType() == 0 we received Merchant else Customer)
Then use their id to extract actual object from relational db.
I searched a lot, but couldn't find anything that can help to estimate if it is a good practice. Is it a good practice?
Please, give me a hint if there is a better way.
There doesn't seem to be anything wrong with what you did unless there is some collusion as mentioned by #Ivan in comments.
Here is another possible way to do if you were using elasticTemplate- Spring Data Elasticsearch: Multiple Index with same Document or if you are using queryBuilder - https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search.html

Stackoverflow-Exception while loading RelationshipEntity in OGM

I have the following classes:
#NodeEntity
public class Item{
//...
}
#RelationshipEntity(type = "HAS")
public class HasRelation{
//...
#StartNode
private User user;
#EndNode
private Item item;
}
#NodeEntity
public class User{
//...
#Relationship(type="HAS")
private Set<HasRelation> has;
}
So now I have a User Sven with ID 1 having an Item Hammer in the Database and want to load it.
When I call the OGM session.load(User.class, 1) I always get an Stackoverflow-Exception, because the User hold a Relationship, holding the User, holding a relationship, and so on.
This feels like the wrong way to use OGM for me and I don't want to restrict the Depth by which I load to 0.
However the OGM specification tells me, that there is no other way, since the RelationshipEntity needs a Start- and EndNode and has to be referenced in one of those.
So I don't see a way to prevent this Exception other than resticting the Loading-Depth to 0.
Is there a better way?
You are exposing the data as JSON. The converter also needs to traverse the
'object tree' and this causes the stackoverflow.
The solution is simple: You are defining an outgoing relationship in the User class so this object does not need to be visited again when the jackson lib hits the relationship.
#RelationshipEntity(type = "LIKES")
public class LikedBook {
#Id
#GeneratedValue
private Long id;
private String how;
#StartNode
#JsonIgnore // <- "do not go back"
private User user;
#EndNode
private Book book;

Using JPA to access a view without a unique id returns null

I am using JPA and have a view I would like to access. I am using a mapped entity with an embedded Id in many of my other classes to access tables with similar requirements. However here whenever there are nulls in the view that comprise the id, the whole object is returned as null. There are the right number of entities returned when i query, but they are null.
Here are the classes:
{
#Entity
#Table(name = "VW_PRODUCT")
public class VwProduct implements Serializable {
#EmbeddedId
private VwProductId id;
public VwProduct() {
}
}
{
#Embeddable
public class VwProductId implements java.io.Serializable {
#Column(name = "PROD_NAME", nullable=true)
private String prodName;
#Column(name = "PROD_CTGRY", nullable=true)
private String prodCtgry;
#Column(name = "PROD_SBCTGRY", nullable=true)
private String prodSbctgry;
}
I omitted things like getters and setters and hashcode but i think my question is clear; how do I access this view, when some of its values are null?
Thank you!
If you have a primary key column with null, and search on that column will always return no objects.
There are three possible solutions that I am aware of, in order of complexity/wonkiness. (For people not working on a read-only view, do not do any of the following. You will blow your foot off with an incredibly large shotgun.)
The easiest answer is to change your definition of the view and add something like a rowid or generated serial and then make that the primary key.
The second answer, and this is both implementation specific and hibernate specific, is to have a primary key of #ID #ROWID String id;
The last answer, is more complex, is by mapping all three of your fields to a "NullableString" UserType, and have a nullSafeGet that maps null to something non-null, like '' or something. You'll get duplicates, but since this is a read-only view, you don't really care.

Criteria API createAlias on column containing null values kills the query

I do have a simple entity named "Address" that has a couple of properties and relations defined by itself and some inherited from some superclass.
public class Base {
private UUID id;
private Date createdAt;
#NotNull
private User createdBy; // User is a related Entity that cannot be null
private DataImport dataImport; // DataImport is another related entity that can be null
//getter & setter
}
public class Address extends Base {
private String street;
#NotNull
private Country country; // related entity that can't be null
//getter & setter
}
What I'm trying to achieve is with one query using the Criteria API, I want to get a list of Address objects containing all simple attributes like street and createdAt. At the same time I want only the IDs of the related entities if present: createdBy.id, dataImport.id and country.id.
I'm almost there using the following Criteria query:
entityManager.getDelegate().createCriteria(Address.class).add(criteriaExample)
.excludeZeroes()
.createAlias("createdBy", "subcreatedBy")
.createAlias("country", "subcountry")
.setProjection(Projections.projectionList().add(Projections.property("id").as("id"))
.add(Projections.property("createdAt").as("createdAt"))
.add(Projections.property("street").as("street")).
.add(Projections.property("subcreatedBy.id").as("createdBy.id"))
.add(Projections.property("subcountry.id").as("country.id")))
.setResultTransformer(new AliasToBeanNestedResultTransformer(Address.class));
List<Address> result = criteria.list();
This works just perfect!
Problem occurs when I only add the "alias" for the dataImport relation.
...createAlias("dataImport", "subdataImport")...
Even without adding the Projection for dataImport.id to the query, it returns an empty list, meaning list.size() = 0, as soon as I add this alias.
My current guess is, that I can't put an alias on a nullable property. Does anybody have an idea what the solution might be? So, when the related entity is not null, I want to get it's ID. And I want it to be simply null, when the relation is not set.
Thanks in advance.
Stupid me should have read the documentation and set the CriteriaSpecification.LEFT_JOIN.
...createAlias("dataImport", "subdataImport", CriteriaSpecification.LEFT_JOIN)...

Categories