User Model
#Entity
#Table(name="user")
public class User extends Model
{
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
public int id;
public String name;
public int age;
}
In my controller, trying to update the user submitted form
Form<User> userForm = Form.form(User.class).bindFromRequest();
Updating one of the property of form object, using following snippet.
userForm.data().put("name","updated_name"); // Trying to update name field.
Verifying, whether the change is reflected or not. Actually it's getting updated by verifying its log.
Logger.info("submitted form :: "+userForm.toString()); // Form(of=class models.User, data={name=updated_name, age=25}, value=Some(models.User#768d00), errors={})
User user = userForm.get();
// But here it returning only the original value.
Logger.info("name :: "+user.name); // Getting Old name.
For updating the name property in my controller i can directly update like following
user.name = "updated_name", but i just want to know why the above approach not working?
Related
I'm currently learning Spring-Boot and Spring-Data-JPA.
I'm using a postgresql database for storing the data.
My goal is to store ingredients with a unique and custom ID (you just type it in when creating it), but when another ingredient with the same ID gets inserted, there should be some kind of error. In my understanding, this is what happens when I use the #Id annotation, hibernate also logs the correct create table statement.
This is my Ingredient class:
public class Ingredient {
#Id
#Column(name = "ingredient_id")
private String ingredient_id;
#Column(name = "name")
private String name;
#Column(name = "curr_stock")
private double curr_stock;
#Column(name = "opt_stock")
private double opt_stock;
#Column(name = "unit")
private String unit;
#Column(name = "price_per_unit")
private double price_per_unit;
#Column(name = "supplier")
private String supplier;
-- ... getters, setters, constructors (they work fine, I can insert and get the data)
}
My controller looks like this:
#RestController
#RequestMapping(path = "api/v1/ingredient")
public class IngredientController {
private final IngredientService ingredientService;
#Autowired
public IngredientController(IngredientService ingredientService) {
this.ingredientService = ingredientService;
}
#GetMapping
public List<Ingredient> getIngredients(){
return ingredientService.getIngredients();
}
#PostMapping
public void registerNewStudent(#RequestBody Ingredient ingredient) {
ingredientService.saveIngredient(ingredient);
}
}
And my service class just uses the save() method from the JpaRepository to store new ingredients.
To this point I had the feeling, that I understood the whole thing, but when sending two post-requests to my application, each one containing an ingredient with the id "1234", and then showing all ingredients with a get request, the first ingredient just got replaced by the second one and there was no error or smth. like that in between.
Sending direct sql insert statements to the database with the same values throws an error, because the primary key constraint gets violated, just as it should be. Exactly this should have happened after the second post request (in my understanding).
What did I get wrong?
Update:
From the terminal output and the answers I got below, it is now clear, that the save() method can be understood as "insert or update if primary key is already existing".
But is there a better way around this than just error-handle every time when saving a new entry by hand?
The save method will create or update the entry if the id already exists. I'd switch to auto generating the ID when inserting, instead of manually creating the IDs. That would prevent the issue you have
When saving a new ingredient, jpa will perform an update if the value contained in the “id” field is already in the table.
A nice way through which you can achieve what you want is
ingredientRepository.findById(ingredientDTO.getIngredientId()).
ifPresentOrElse( ingredientEntity-> ResponseEntity.badRequest().build(), () -> ingredientRepository.save(ingredientDTO));
You can return an error if the entity is already in the table otherwise (empty lambda), you can save the new row
This is a downside to using CrudRepository save() on an entity where the id is set by the application.
Under the hood EntityManager.persist() will only be called if the id is null otherwise EntityManager.merge() is called.
Using the EntityManager directly gives you more fine grained control and you can call the persist method in your application when required
Hello everyone and thank you in advance for looking at this question. I am using a Couchbase Server community-6.6.0 on Java Spring Boot (Java JDK version 1.8).
Problem Description: To interact with Couchbase I am using a PagingAndSortingRepository Repository. Each time I call .save() a new document representing a new instance of the Object I am using gets created in Couchbase. Also, when I retrieve an Object from Couchbase change the value of some field e.g., .setPassword() and then call .save() to make the change persistent then I get a new document (with a new Id) of that Object is created in Couchbase. It seems to me that many different revisions of that Object exist in the Database when I call .save(), I want to make sure that I am always working on the latest version of that instance of my Object/Entity.
I am really confused! What I am trying to do should be straightforward, every time I update a Java Entity I want the changes to be made persistent for that Document (with that Document id) in Couchbase. Perhaps I am missing something here. Please advice.
Partial view of the pom looks like:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-couchbase</artifactId>
</dependency>
Entities as follows:
#Data
#Document
#CompositeQueryIndex(fields = {"id", "userName"})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#IdPrefix
private String prefix;
#Id #NotNull #GeneratedValue( delimiter = "::", strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
#NotNull
#Size(min=2, max=40)
private String firstname;
#Field
#NotNull
#QueryIndexed
private String username;
//Other attributes here ...
}
//ConfirmationToken Entity
#Data
#NoArgsConstructor
#CompositeQueryIndex(fields = {"id", "confirmationToken"})
public class ConfirmationToken {
#IdPrefix
private String prefix = "token";
#Id #NotNull #GeneratedValue(delimiter = "::", strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
#Reference
private User user;
#Field
#QueryIndexed
private String confirmationToken;
//Other attributes here ...
}
UserRepository looks like:
#Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
#ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List<User> findByUsername(String username);
}
#Service
#AllArgsConstructor
public class ConfirmationTokenServiceImpl implements ConfirmationTokenService {
#Autowired
private final ConfirmationTokenRepository confirmationTokenRepository;
#Override
public void saveConfirmationToken(ConfirmationToken confirmToken) {
confirmationTokenRepository.save(confirmToken);
}
}//end class
When I want to retrieve the User Object from Couchbase I do the following:
//How do I ensure this Object is the latest version of that document?
List<User> users = userRepository.findByUsername(username);
//The following does not ensure that I get the latest version of that User document with that username
User testUser = users.get(users.size()-1);
In some method I change field values of that Java Object i.e., User and then a new revision of that User Document gets created in the Database when I call .save(User Object). How do I update the fields of that User object and make that Document persistent in the Database without creating a new revision of that Document? Also, how do I ensure I always get and work on the latest version of that User Document?
public void someMethod(UserDTO regForm, final String contextPath) {
User testUser = new User();
testUser.setFirstname(regForm.getFname());
testUser.setLastname(regForm.getSname());
//Make User persistent
userRepository.save(testUser);
//Create the confirmation token for that User instance
final ConfirmationToken confirmToken = new ConfirmationToken(testUser);
confirmationTokenService.saveConfirmationToken(confirmToken);
}
In this Method I have another Class called ConfirmationToken that has a reference to a User Object. This is to associate each ConfirmationToken instance with a specific User Instance. When I call the following method to change the User password a new document with a new id is created for the User in the Database. I cannot understand how to update the current instance. Please help.
public void changeUserPassword(ConfirmationToken confirmToken, ResetPassDTO resetPassForm) {
//Retrieve the User instance that is associated with that Token
final User testUser = confirmToken.getUser();
if (testUser.getEmail().equals(resetPassForm.getEmail())) {
//Get the Hash of the User's password
final String encryptedPassword = bCryptPasswordEncoder.encode(resetPassForm.getNewPassword());
testUser.setPassword(encryptedPassword);
//Enable the user account - since email ownership is also verified
testUser.setIsEnabled(true);
userRepository.save(testUser);
}
//Delete the Confirmation Token entity
confirmationTokenService.deleteConfirmationToken(confirmToken);
}
Update:
It seems that I had to go back and work on my domain modelling for the Entities. More specifically to think of how couchbase is doing docs embedding and when to do referencing. In my example, the ConfirmationToken instance holds a reference to a User instance. In reality the ConfirmationToken embeds the User instance in the same JSON doc. I cannot access the actual User object I want though. In other words, it seems that I cannot dereference and get the User instance by following the id of User object from the ConfirmationToken instance. My solution: I introduced a uuid field in the User object, I then get the reference for a User instance from ConfirmationToken, then I call userRepository.findByUuid(userFromToken.getUuid()); only then I was able to get the actual User object that I wanted. My question is why Couchbase spring SDK cannot do this dereferencing automatically? Also, it seems that when I change the value of some attribute the change does not happen on the actual object but on the embedding, so every time I need to do this dereferencing to get to the real object manually. This is similar to MongoDB #DBRefs?? Not sure how if there is a solution with N1QL though and NEST queries? Can I achieve this with N1QL?
I have an entity class with fields of type org.bson.Document. These are values that I am not allowed to modify, but when using Spring Data I need to map them in my model class so that after saving the document back to Mongo these values won't be lost. So the document is fetched from Mongo, mapped to a User POJO and then passed to a Thymeleaf form. When I try to send Thymeleaf form back to the controller I get 400 Bad Request "Validation failed for object..." error and I know it's because of these two additional Document fields. How can I pass these fields to Thymeleaf and then back to the controller? They aren't modified in the form, just appear as hidden inputs:
<input id="resetPassword" th:field="${user.resetPassword}" type="hidden"/>
<input id="consents" th:field="${user.consents}" type="hidden"/>
And my User class:
#Data
#Document(collection = "users")
#NoArgsConstructor
public class User {
#Id
private ObjectId id;
private String email;
private String name;
private String surname;
private String phone;
private String password;
private String country;
private SecurityLevel securityLevel = SecurityLevel.LOW;
private Timestamp created = Timestamp.from(Instant.now());
private Boolean blocked = false;
private org.bson.Document resetPassword;
private org.bson.Document consents;
}
It sounds like the object is being successfully injected into the Thymeleaf template, but not parsed correctly in Spring when the form is returned.
You should examine the representation in the web page (expecting json?) and then ensure that you have a handler defined in Spring that can successfully deserialise the returned object.
If the Document type does not have a conventional constructor (no-args or all-args), or some of the fields are 'hidden' (without the standard getXxx and setXxx methods), then Spring will not be able to reconstruct the object when the form is submitted without a custom handler.
Similarly, if there are not getters for all of the fields (And sub fields) of the object, the Thymeleaf template will have an incomplete object embedded that will not upload correctly.
Take a look at this blog post for some further info: https://www.rainerhahnekamp.com/en/spring-mvc-json-serialization/
I solved it by creating a custom Formatter like that:
public class BsonDocumentFormatter implements Formatter<Document> {
#Override
public Document parse(String s, Locale locale) throws ParseException {
return Document.parse(s);
}
#Override
public String print(Document document, Locale locale) {
return document.toJson();
}
}
And then I registered it in my WebMvcConfigureruration:
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new BsonDocumentFormatter());
}
So I have looked at various tutorials about JPA with Spring Data and this has been done different on many occasions and I am no quite sure what the correct approach is.
Assume there is the follwing entity:
package stackoverflowTest.dao;
import javax.persistence.*;
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
public Customer(String name) {
this.name = name;
}
public Customer() {
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
We also have a DTO which is retrieved in the service layer and then handed to the controller/client side.
package stackoverflowTest.dto;
public class CustomerDto {
private long id;
private String name;
public CustomerDto(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
So now assume the Customer wants to change his name in the webui - then there will be some controller action, where there will be the updated DTO with the old ID and the new name.
Now I have to save this updated DTO to the database.
Unluckily currently there is no way to update an existing customer (except than deleting the entry in the DB and creating a new Cusomter with a new auto-generated id)
However as this is not feasible (especially considering such an entity could have hundreds of relations potentially) - so there come 2 straight forward solutions to my mind:
make a setter for the id in the Customer class - and thus allow setting of the id and then save the Customer object via the corresponding repository.
or
add the id field to the constructor and whenever you want to update a customer you always create a new object with the old id, but the new values for the other fields (in this case only the name)
So my question is wether there is a general rule how to do this?
Any maybe what the drawbacks of the 2 methods I explained are?
Even better then #Tanjim Rahman answer you can using Spring Data JPA use the method T getOne(ID id)
Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
Is's better because getOne(ID id) gets you only a reference (proxy) object and does not fetch it from the DB. On this reference you can set what you want and on save() it will do just an SQL UPDATE statement like you expect it. In comparsion when you call find() like in #Tanjim Rahmans answer spring data JPA will do an SQL SELECT to physically fetch the entity from the DB, which you dont need, when you are just updating.
In Spring Data you simply define an update query if you have the ID
#Repository
public interface CustomerRepository extends JpaRepository<Customer , Long> {
#Query("update Customer c set c.name = :name WHERE c.id = :customerId")
void setCustomerName(#Param("customerId") Long id, #Param("name") String name);
}
Some solutions claim to use Spring data and do JPA oldschool (even in a manner with lost updates) instead.
Simple JPA update..
Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);
This is more an object initialzation question more than a jpa question, both methods work and you can have both of them at the same time , usually if the data member value is ready before the instantiation you use the constructor parameters, if this value could be updated after the instantiation you should have a setter.
If you need to work with DTOs rather than entities directly then you should retrieve the existing Customer instance and map the updated fields from the DTO to that.
Customer entity = //load from DB
//map fields from DTO to entity
So now assume the Customer wants to change his name in the webui -
then there will be some controller action, where there will be the
updated DTO with the old ID and the new name.
Normally, you have the following workflow:
User requests his data from server and obtains them in UI;
User corrects his data and sends it back to server with already present ID;
On server you obtain DTO with updated data by user, find it in DB by ID (otherwise throw exception) and transform DTO -> Entity with all given data, foreign keys, etc...
Then you just merge it, or if using Spring Data invoke save(), which in turn will merge it (see this thread);
P.S. This operation will inevitably issue 2 queries: select and update. Again, 2 queries, even if you wanna update a single field. However, if you utilize Hibernate's proprietary #DynamicUpdate annotation on top of entity class, it will help you not to include into update statement all the fields, but only those that actually changed.
P.S. If you do not wanna pay for first select statement and prefer to use Spring Data's #Modifying query, be prepared to lose L2C cache region related to modifiable entity; even worse situation with native update queries (see this thread) and also of course be prepared to write those queries manually, test them and support them in the future.
I have encountered this issue!
Luckily, I determine 2 ways and understand some things but the rest is not clear.
Hope someone discuss or support if you know.
Use RepositoryExtendJPA.save(entity). Example:
List<Person> person = this.PersonRepository.findById(0)
person.setName("Neo");
This.PersonReository.save(person);
this block code updated new name for record which has id = 0;
Use #Transactional from javax or spring framework. Let put #Transactional upon your class or specified function, both are ok. I read at somewhere that this annotation do a "commit" action at the end your function flow. So, every things you modified at entity would be updated to database.
There is a method in JpaRepository
getOne
It is deprecated at the moment in favor of
getById
So correct approach would be
Customer customerToUpdate = customerRepository.getById(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
I am developing an application which uses Spring-boot, a relational database and Elasticsearch.
I use JSON serialization at 2 differents places in the code:
In the response of the REST API.
When the code interacts with Elasticsearch.
There are some properties that I need in Elasticsearch but that I want to hide to the application user (e.g. internal ids coming from the relational database).
Here is an example of entity :
#Document
public class MyElasticsearchEntity {
#Id
private Long id; //I want to hide this to the user.
private String name;
private String description;
}
Problem : When the object it persisted in Elasticsearch, it gets serialized as JSON. Hence, fields with #JsonIgnore are ignored when serialized to Elasticsearch.
Up to now, I found 2 unsatisfying solutions :
Solution 1 : Use #JsonProperty like this :
#Id
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
The id gets written in Elasticsearch and is nullified in the JSON response :
{
"id" : null,
"name" : "abc",
"description" : null
}
So it works but the application user still sees that this property exists. This is messy.
Solution 2 : Cutomize the object mapper to ignore null values
Spring-boot has a built-in option for that :
spring.jackson.serialization-inclusion=NON_NULL
Problem : it suppresses all non-null properties, not only those that I want to ignore. Suppose that the field description of the previous entity is empty, the JSON response will be :
{
"name" : "abc"
}
And this is problematic for the UI.
So is there a way to ignore such field only in the JSON response?
You could use Jackson JsonView for your purpose. You can define one view which will be used to serialize pojo for the application user :
Create the views as class, one public and one private:
class Views {
static class Public { }
static class Private extends Public { }
}
Then uses the view in your Pojo as an annotation:
#Id
#JsonView(Views.Private.class) String name;
private Long id;
#JsonView(Views.Public.class) String name;
private String publicField;
and then serialize your pojo for the application user using the view:
objectMapper.writeValueUsingView(out, beanInstance, Views.Public.class);
This is one example of many others on how view can fit your question. Eg. you can use too objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false); to exclude field without view annotation and remove the Private view.