I'm trying to store an JSON Array in MySQL with Spring and Hibernate.
The application gets the likes from the users and stores the id of a user in a JSON Array, for example
[1,2,3,4,5,6]
in a column name likeswith type text (MySQL)
¿How can I put a new id in the array? I mean, get the JSON form database and insert the id and count the number of elements.
I'd use transient variables as a solution for this problem. I don't think there is a better solution for this. You may also try hibernate interceptors for this as a solution.
#Table
#Entity(name='Likes')
public class LikesEntity implements Serializable {
#Transient
private List<String> userIds;
#Column
private String likes;
#Column
private String count;
//getter and setters for the like and count
...
public void addUserId(String id){
this.userIds.add(userIds);
this.likes = convertToJsonArray(userIds); // use GSON or Jackson or any other library that can help you convert array to JSON string
this.count = this.userIds.size();
}
}
within your service layer do the following
#Transactional(lockmode = LockMode.READ){
LikesEntity le = sess.get(LikesEntity.class,234);
le.addUserId("userPk");
session.saveOrUpdate(le);
}
Related
I have an entity 'Product' and I want the primary key in ES to be used as a combination of 'id' and 'name' attributes. How can we do that using spring data elastic search.
public class Product {
#Id
private String id;
#Id
private String name;
#Field(type = FieldType.Keyword)
private Category category;
#Field(type = FieldType.Long)
private double price;
#Field(type = FieldType.Object)
private List<ValidAge> age;
public enum Category {
CLOTHES,
ELECTRONICS,
GAMES;
}
}
One way to achieve this would be the following:
first rename your id property, I changed it to documentId here. This is necessary, because in Spring Data
Elasticsearch an id-property can be either annotated with #Id or it can be namend id. As there can only be one
id-property we need to get this out of the way. It can have the name id in Elasticsearch, set by the #Field
annotation, but the Java property must be changed.
second, add a method annotated with #Id and #AccessType(AccessType.Type.PROPERTY) which returns the value you
want to use in Elasticsearch.
third, you need to provide noop-setter for this property. This is necessary because Spring Data Elasticsearchsoe
not check the id property to be read only when populating an entity after save or when reading from the index.
This is a bug in Spring Data Elasticsearch, I'll create an issue for that
So that comes up with an entity like this:
#Document(indexName = "composite-entity")
public class CompositeEntity {
#Field(name="id", type = FieldType.Keyword)
private String documentId;
#Field(type = FieldType.Keyword)
private String name;
#Field(type = FieldType.Text)
private String text;
#Id
#AccessType(AccessType.Type.PROPERTY)
public String getElasticsearchId() {
return documentId + '-' + name;
}
public void setElasticsearchId(String ignored) {
}
// other getter and setter
}
The repository definition would be straight forward:
public interface CompositeRepository extends ElasticsearchRepository<CompositeEntity,
String> {
}
Remember that for every method that needs an Elasticsearch Id, you'll need to create like it's done in the entity
class.
I am not sure about spring data elasticsearch but spring jpa provides the facility of defining composite primary key by using #IdClass where we can define a separate class(let us say class A) in which we can define all the fields which we want to be a part of composite key Then we can use #IdClass(A.class) in entity class and use #Id annotation on all the fields which should be the part of the composite key
you can refer to this article, although I am not sure whether the same concept will be applicable for spring data es - https://www.baeldung.com/jpa-composite-primary-keys
This question is very similar to Get ID of last inserted document in a mongoDB w/ Java driver with one difference: I'm using a typed / generic collection.
Example DTO:
public class ForumMessageDTO {
#Expose
#BsonId
private ObjectId id;
private Long forumId;
#Expose
private Long userId;
#Expose
private Date created;
#Expose
private String message;
/* getters and setters are not shown here but they are implemented.... */
}
Example code for inserting a document:
public ForumMessageDTO addMessage(Long forumId, Long userId, String message) {
ForumMessageDTO dto = new ForumMessageDTO(forumId, userId, new Date(), message);
messages.insertOne(dto);
return dto; /* dto.id is null here!!! But why? */
}
The returned dto should have its id field filled in, because it was annotated with #BsonId and it has ObjectId type. In reality, it remains null and I don't see how I could access the ObjectId of the inserted document.
This version of collection.insertOne does not return anything, and apparently it does not change the id field of the dto.
Probably I could manually convert the DTO into a Document and use that version of collection.insertOne, and then get the object id and put it back into the DTO but this is very inefficient. Considering the fact that I'm going to use many collections with many different DTO classes, and I do not want to write manual conversions for all of them.
So how can I retrieve the object id of the document that was just inserted?
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
In MongoDB documentation they suggest to use ObjecId for manual references.
please see https://docs.mongodb.com/manual/reference/database-references/#document-references
original_id = ObjectId()
db.places.insert({
"_id": original_id,
"name": "Broadway Center",
"url": "bc.example.net"
})
db.people.insert({
"name": "Erin",
"places_id": original_id,
"url": "bc.example.net/Erin"
})
I'm using spring-data-mongodb and what I'm looking for is to have a People class defined like this:
#Document
public class People {
private String name;
#Reference // or any Annotation to convert an ObjectId to a String
private String placesId;
private String url;
}
How to have a "places_id" as ObjectId in mongoDB but mapped to a String in our POJO ?
I was expecting to have an annotation like #Reference but it seems to not be implemented.
I don't understand why we don't have this kind of annotation in spring-data-mongodb. I don't want to implement an explicit converter like suggested in spring documentation for all documents that use manual references.
Maybe it's not the right approach.
Did I miss something ?
UPDATE :
I like the idea to have a POJO using only String instead of ObjectId. Let's say I've got a class Place like this :
#Document
public class Place {
#Id
private String id;
private String name;
}
place.getId() will be a String but people.getPlaceId() will be an ObjectId. I want to avoid this unnecessary mapping.
The solution would be:
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
public class People {
#Field(targetType = FieldType.OBJECT_ID)
private String placesId;
}
This will map POJO string to ObjectId in MongoDB.
Why don't you leave the field as ObjectId?
#Document
public class People {
private String name;
private ObjectId placesId;
private String url;
}
If you want to query by this field you can do this:
For lists
List<String> ids // the ids as strings
List<ObjectId> objIds = ids .stream()
.map(i -> new ObjectId(i))
.collect(Collectors.toList());
For single String
String id // single id
ObjectId objId = new ObjectId(id);
If you want to make a real reference to an other object in your database, use the #DBRef annotation which is provided by Spring Data.
Your updated code could look like the following:
#Document
public class People {
private String name;
#DBRef
private Place place;
private String url;
}
Spring Data will then automatically map a Place object to your People object. Internally this is done with a reference to the unique ObjectId. Try this code and have a look at your mongo database.
For more information have a look at: MongoDb with java foreign key
I have a solution very simple:
#JsonSerialize(using= ToStringSerializer.class)
private ObjectId brandId;
...
put that on the attribute that is Object Id, and the ObjectId gets and inserts like string
In Spring Data Jpa we have a flexibility to map the required fields query result to a VO object with out exposing the entity object outside.
DB Used :: mysql
1st way ::
#Query("SELECT new CountryVO(c.id,c.name) FROM Country c")
public List<CountryVO> getCountries();
//Country Entity
public class Country{
private long id;
private String name;
#DBRef
private State state;
}
// State Entity
public class State{
private long id;
private String name;
}
//Country VO
public class CountryVO{
private long id;
private String name;
private String stateName;
}
2nd way ::
#Query("SELECT DISTINCT c.name FROM Country c")
public List<String> getNames();
Now my point is I am using Spring Data mongoDB with mongoDB database and here the querying way is different like below
#Query(value = "{}", fields = "{'id':1,'name':1,'state.name':1}")
List<CountryVO> getAllCountries();
In the above query we can mention what ever fields we want in the fields attribute and the remaining fields will come as null values, but I want to map the output result to a VO like Spring Data Jpa.
Please let me know any possibilities
Thanks in Advance
Just use your CountyVO as return type:
#Query(value = "{}", fields = "{'id':1,'name':1}")
List<CountryVO> getAllCountries();