How to enforce unique field with MongoDB in Spring - java

I have a pojo with two fields that need to be unique, the id and the email. So I added the #Indexed(unique = true) annotation to the necessary fields like so
public class User {
#Id
#Indexed(unique = true)
private String id;
#Indexed(unique = true)
private String email;
private int money;
I then tested it out and it was not enforced. So I googled about and I found a previous answer here - Spring Data: Unique field in MongoDB document and subsequently deleted the collection, added spring.data.mongodb.auto-index-creation=true to my application.properties file and tried again.
However, the unique field still isn't enforced! I see there is another answer using ensureIndex() but it also has a great comment that was never answered- Why do we need to use the annotation if all the work is done on mongoTemplate?
So since the question is old enough that apparently the only working answer is depreciated (the new way is using createIndex()), I thought it was time for a new version. Is it possible to require a column in a mongo collection to be unique from Spring Boot?

Related

How does an existing object retrieve it's changes once we update it somewhere else in repository? [duplicate]

I have a JpaRepository persisting newly created entity in Spring MVC app. This entity looks like this (very simplified):
#Entity
public class Translation {
.....
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
private Version version;
....
}
and Version entity:
#Entity
public class Version {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#Column(name = "version_code")
private long code;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "version", cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Translation> translations;
}
I create a translation object like this
TranslationDTO t = new TranslationDTO();
t.setText(translationText);
ClientVersionDTO version = new ClientVersionDTO();
version.setId(11);
t.setVersion(version);
where 11 is a version that exists in the database already from the very beginning. Please notice that I do not set values for name and code of ClientVersionDTO.
Then I have a service that persists new object (I use dozer library to convert DTO to entities)
#Service
#Transactional
public class TranslationsServiceImpl implements TranslationsService {
#Override
public Long create(TranslationDTO translationDTO) {
Translation translation = translationsConverter.unconvert(translationDTO);
Translation t = translationRepository.saveAndFlush(translation);
Translation t2 = translationRepository.findOne(t.getId());
// !!!! t2.getVersion() returns version where no values are set to 'code' and 'name'
return t2.getId();
}
}
Please notice my comment "t2.getVersion() returns version where no values are set to 'code' and 'name'" - I was expecting so that when I fetch the data from the database, I would get a Version object right from the database with code and name values set. However they are not set. So basically what I get as a t2.getVersion() object is the same object as in input argument translationDTO.getVersion(). How can they I re-invalidate the Version object?
UPDATE tried moving #Transactional to JpaRepository, but still the same result.
If you are using Hibernate, this is the expected result. When you call translationRepository.saveAndFlush(translation) and translationRepository.findOne(t.getId()) one after the other, they hit the same Hibernate session which maintains a cache of all objects that it has worked on. Therefore, the second call simply returns the object passed to the first. There is nothing in those two lines that would have forced Hibernate to fire a SELECT query on the database for the Version entity.
Now, the JPA spec does have a refresh method on the EntityManager interface. Unfortunately, Spring Data JPA does not expose this method using its JpaRepository interface. If this method was available, you could have done t = translationRepository.saveAndFlush(translation) and then versionRepository.refresh(t.getVersion()) to force the JPA provider to synchronize the version entity with the database.
Implementing this method is not difficult. Just extend SimpleJpaRepository class from Spring Data JPA and implement the method yourself. For details see adding custom behaviour to all Spring Data JPA repositories.
An alternate would be to load the version entity as versionRepository.findOne(version.getId()) before setting it on the translation. Since you can hard-code version id in your code, your versions seem to be static. You can therefore mark your Version entity as #Immutable and #Cacheable (the former is a Hibernate-specific annotation). That way, versionRepository.findOne(version.getId()) should not hit the database every time it is called.

Saving ENUM in spring data elasticsearch

I am trying to save my entity in elasticsearch using spring data elasticsearch, all the attributes are saved (including objects) except for enum its always stored as null, this is my entity
#Entity
#Document(indexName="invoices", type="invoices", shards = 1)
public class Invoice {
#Transient
#JsonIgnore
#org.springframework.data.annotation.Id
private String searchIndex;
#Field(type = FieldType.String)
private InvoiceStateEnum state;
with and without #Field attribute state is being saved as null even though the object is being saved has value for this enum.
Any help is appreciated
As spring-data-elasticsearch uses Jackson, you can put the #JsonFormat.Shape.STRING annotation to your enum:
#JsonFormat.Shape.STRING
public enum InvoiceStateEnum {
// your enum code
}
I was able to solve the issue by removing folder data under my project and rerun the application, seems like for some reason elastic search was not updating the records so I was getting null since the attribute was added recently.

Override Spring data JPA id annotation.

I'm using Spring-data-Jpa where I've an entity
#Entity(name="person")
public class Person implements Serializable {
#javax.persistence.Id
private long dbId;
#Id
private final String id;
// others attributes removed
}
In above class I've two different ids id (marked with org.springframework.data.annotation.Id) and dbId(marked with javax.persistence.Id) , since my id field is always populated with a unique identifier (for Person class which I'm getting from somewhere else) so while using Spring JpaRepository it always tries to update the record and since it's not in db, nothing happens.
I've debug code and saw that it uses SimpleKeyValueRepository which gets the id field which is id, and thus it always gets a value and tries to update record, can I override this behavior to use dbId instead of id field? Is there any way to achieve same with some configuration or annotation, any help is greatly appreciated.
Each entity must have exactly one #Id. On the other hand, you might want to declare a column as unique. It can be done by:
#Entity(name="person")
public class Person implements Serializable {
#Id
private Long id;
#Column(unique = true)
private final String uuid;
// others attributes removed
}
Also remember, that Spring Data JPA id should be reference Long instead of a primitive as you want to save objects with id = null.
String id should probably be String uuid and be initialized as String uuid = UUID.randomUUID().toString();
Similar situation would be an unique email requirement for user. On one hand it'll be a primary key, but on the other, you won't mark it as #Id.
If you need further clarification or your environment is more complicated, just ask in comments section below.

Spring JPA + Hibernate Search : How update the Search Index(Lucene) only?

I use Spring JPA + Hibernate Search to implement persistant and search in my application.
I have models like this
public class FeatureMeta {
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique=true)
private String uri;
#Column
#Field
private String name;
#Field
#Column
private String businessDesc;
#Field
#Column
private String logicalDesc;
.
.
#Field
#Column(insertable=false,updatable=false)
private Long totalDownloads;
.
.
}
To give the idea about this class, "FeatureMeta" maintains meta-data information which updates very rarely.
However the field "totalDownloads" is constantly changing whenever user download information about this "feature".
Basically "totalDownloads" is not part of the meta-data but I had to put this field in the model because I need to show the "totalDownloads" in the search result of "feature search".
I use same JPA Repository which updates both MySQL and Lucene index.
My question is ; Is it possible to only update the "totalDownloads" in the Lucene Index but not the entity in MySQL whenever change is done to the "totalDownloads" field ?
You'll have to use the #Transient annotation to mark that you don't want this attribute part of your database model.
#Field
#Transient
private Long totalDownloads;
Making the field transient also means it won't be loaded from the database (completely ignored by Hibernate ORM, but not by Hibernate Search); if that's not what you intended you could add an additional field: map one to Hibernate ORM and the other indexed with Hibernate Search and annotated with #Transient. In this case you'll have to make the setter update both fields.
You will likely need to change this configuration property too:
hibernate.search.enable_dirty_check = false
as Hibernate Search will otherwise not generate any change in the Lucene index, in case the entity has no other changes.

eclipselink struct fields order

I am working on a EclipseLink application, which uses Oracle Objects as IN and OUT parameters (while invoking stored procedure). As you know we have #Struct annotations available in Eclipselink for representing Oracle Object, I used it and it is working perfectly. But, looks like order of the fields declared in Struct annotated class matters a lot to map to correct field in oracle object. This causes maintenance issues and very difficult to code when object's properties are more. Is there a way in Eclipselink to say map Structure fields based on name and not with order.
Ex: Below is my Struct class. If by chance I declare variables in different order from fields list, wrong/incorrect mappings will happen while fetching records from stored proc. Its always mapping values to fields from top to bottom. #Column name annotation is not able to solve this issue.
#Struct(name = "REC_OBJECT",
fields = {"TRANS_ID", "PROJECT_ID", "LANGUAGE_CODE", "DESCRIPTION"})
#Embeddable
public class Master {
#Column(name = "PROJECT_ID")
private String projectId;
#JsonIgnore
#Column(name = "TRANS_ID")
private String transactionId;
#Column(name = "LANGUAGE_CODE")
private String languageCode;
#Column(name = "DESCRIPTION")
private String description;
}
Please suggest solution for this. Thank you.

Categories