I would like someone to help me on how to update row with entity manager. Here is a table ex, in angular where data is sent to rest service:
app.html
<tr *ngFor="let myCar of cars$ | paginate: { itemsPerPage: count, currentPage: p }; let i = index">
<td>{{ (p - 1) * count + i + 1 }}</td>
<td>{{myCar.name}}</td>
<td>{{myCar.price}}</td>
<td><button type="submit" class="btn btn-secondary" (click)="fillForm(myCar)">
<i class="glyphicon glyphicon-edit"></i>Edit
</button></td>
</tr>
carsDTO.java
#Id
#Column(name = "name")
private String name;
#Column(name = "price")
private String price;
service.java
public carsDTO updateCar(carDTO cars){
TypedQuery<myquerycalss> query = entitymanager.createNamedQuery("sqlName",
myquerycalss.class);
// I need help to complete this update method
// Maybe no need to first find by id, the row gets update based on #id
// on the name
}
resource.java
#PUT
#Path("/updatecars")
public Response updateCar(){
// no preblem here
}
Note: You can see that in the app.html I have ID generated but my jave class just name and price variables.
What is the best approach to update a chosen entity, that is, fields of database record, in my service.java? My resouces url is without parameter, that is URL: .../updatecars
Your resource needs to receive the car selected and changed in the frontend.
You can change it to receive inside the request body, using this:
#RequestMapping(method = RequestMethod.PUT, value = "/updatecars")
public Response updateCar(#RequestBody() carDTO car){
// here you call the service, passing the car as parameter
service.updateCar(car);
}
Inside your angular component, you have to put the car selected in the http request. Something like this:
saveCar(car: carDTO){
return this.httpBase.put(this.url, car, this.options)
.map(dados => dados.json()); // handle the return here....
}
Inside your service:
public carsDTO updateCar(carDTO cars) {
TypedQuery<myquerycalss> query = entitymanager.createNamedQuery("sqlName", myquerycalss.class);
query.setParameter("name", cars.getName());
query.setParameter("price", cars.getPrice());
query.executeUpdate();
...
}
I'm assuming that your named query SQL is like this:
UPDATE cars SET price = :price WHERE name = :name
Related
I need choose values from one array and assign it to other array. Using Spring Thymeleaf. No idea how retrieve these choosed values.
My classes:
#Entity
public class Collaborator {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size (min=3, max=32)
private String name;
#NotNull
#ManyToOne (cascade = CascadeType.ALL)
private Role role;
public Collaborator() {}...
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 3, max = 99)
private String name;
public Role() {}....
My controllers:
#RequestMapping("/project_collaborators/{projectId}")
public String projectCollaborators(#PathVariable Long projectId, Model model) {
Project project = mProjectService.findById(projectId);
List<Collaborator> allCollaborators = mCollaboratorService.findAll();
List<Collaborator> assignments = new ArrayList<>();
if (project.getRolesNeeded()!=null) {
for (int i=0;i<project.getRolesNeeded().size();i++) {
assignments.add(new Collaborator("Unassigned", project.getRolesNeeded().get(i)));
assignments.get(i).setId((long) 0);
}
}
model.addAttribute("assignments", assignments);
model.addAttribute("allCollaborators", allCollaborators);
model.addAttribute("project", project);
return "project_collaborators";
}
#RequestMapping(value = "/project_collaborators/{projectId}", method = RequestMethod.POST)
public String projectCollaboratorsPost(#ModelAttribute Project project, #PathVariable Long projectId, Model model) {
Project p = mProjectService.findById(projectId);
//mProjectService.save(project);
return "redirect:/project_detail/{projectId}";
}
And template:
<form th:action="#{'/project_collaborators/' + ${project.id}}" method="post" th:object="${project}">
<label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
<ul class="checkbox-list">
<li th:each="a : ${assignments}">
<span th:text="${a.role.name}" class="primary">Developer</span>
<div class="custom-select">
<span class="dropdown-arrow"></span>
<select th:field="${a.id}">
<option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
</select>
</div>
</li>
</ul>
<div class="actions">
<input type="submit" value="Save" class="button"/>
Cancel
</div>
</form>
As you can see I want to let user choose for each role (roleNeeded) any collaborator from (allCollaborators) and keep that assigns in List (assignments).
And I get error message:
ava.lang.IllegalStateException: Neither BindingResult nor plain target
object for bean name 'a' available as request attribute
So question is: how to solve it, assign values from one array to another in template and retrieve that values in my controller.
The cause of the exception
The IllegalStateException you are getting is because th:field="${a.id}" in your select element must be related to the form th:object="${project}" element; the th:field attribute must refer to an actual field in the project instance (also you need to write th:field="*{fieldName}"). That should fix the exception you are getting, but will not solve your entire problem as the second part of it is related to how to make the values get into your controller, which I will explain next.
Sending the values to your controller
To get the values into your controller you will need to make a few changes. As I don't really know the code of your Project class, I will change a few things so you will be able to figure it out how to adapt this simple example to your particular case.
First, I understand you want to make a relation like the following in your form:
Role1 => CollaboratorA
Role2 => CollaboratorB
Your controller needs to receive a list and in order to receive this information we need two classes:
The class which will be storing the individual element data, mapping a role id with the collaborator id:
public class RoleCollaborator {
private Long roleId;
private Long collaboratorId;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Long getCollaboratorId() {
return collaboratorId;
}
public void setCollaboratorId(Long collaboratorId) {
this.collaboratorId = collaboratorId;
}
}
A wrapper class to store a list of individual mappings:
public class RolesCollaborators {
private List<RoleCollaborator> rolesCollaborators;
public List<RoleCollaborator> getRolesCollaborators() {
return rolesCollaborators;
}
public void setRolesCollaborators(List<RoleCollaborator> rolesCollaborators) {
this.rolesCollaborators = rolesCollaborators;
}
}
The next thing to do is change your controllers where you have two methods, one that handles GET requests and another one which handles the POST requests and so, receives your form data.
In the GET one:
public String projectCollaborators(#PathVariable Long projectId, Model model) {
(... your code ...)
model.addAttribute("project", project);
// Add the next line to add the "rolesCollaborators" instance
model.addAttribute("rolesCollaborators", new RolesCollaborators());
return "project_collaborators";
}
As you can see, we added a line that will be used by the thymeleaf template. Right now is a wrapper of an empty list of roles and collaborators, but you can add values if you need to edit existing mappings instead of adding new ones.
In the POST one:
// We changed the #ModelAttribute from Project to RolesCollaborators
public String projectCollaboratorsPost(#ModelAttribute RolesCollaborators rolesCollaborators, #PathVariable Long projectId, Model model) {
(... your code ...)
}
At this point, your controller is prepared to receive the information sent from your form, that we also need to modify.
<form th:action="#{'/project_collaborators/' + ${project.id}}" method="post" th:object="${rolesCollaborators}">
<label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
<ul class="checkbox-list">
<li th:each="a, stat : ${assignments}">
<span th:text="${a.role.name}" class="primary">Developer</span>
<div class="custom-select">
<input type="hidden" th:id="rolesCollaborators[__${stat.index}__].roleId" th:name="rolesCollaborators[__${stat.index}__].roleId" th:value="${a.role.id}" />
<span class="dropdown-arrow"></span>
<select th:field="*{rolesCollaborators[__${stat.index}__].collaboratorId}">
<option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
</select>
</div>
</li>
</ul>
<div class="actions">
<input type="submit" value="Save" class="button"/>
Cancel
</div>
</form>
Here there are a few changes:
As I pointed out in The cause of the exception you need to change th:object="${project}" to th:object="${rolesCollaborators}", as rolesCollaborator is the instance name from where you will receive the values from your GET controller method and where you will be sending the values to your POST controller method.
I added a hidden input; this input will store the role id that will be send in association to the collaborator id the user picks from the interface using the select element. Take a look at the syntax used.
I changed the th:field value of your select element to refer to a field in the rollesCollaborators object we use in th:object="${rolesCollaborators}" form attribute. This will set the value of the collaborator in the RoleCollaborator element of the RolesCollaborators wrapped list.
With these changes your code will work. Of course, you can improve it with some other modifications, but I tried to not introduce more modifications to focus on your problem.
Is there a way for updating only some fields of an entity object using the method save from Spring Data JPA?
For example I have a JPA entity like this:
#Entity
public class User {
#Id
private Long id;
#NotNull
private String login;
#Id
private String name;
// getter / setter
// ...
}
With its CRUD repo:
public interface UserRepository extends CrudRepository<User, Long> { }
In Spring MVC I have a controller that get an User object for update it:
#RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> updateUser(#RequestBody User user) {
// Assuming that user have its id and it is already stored in the database,
// and user.login is null since I don't want to change it,
// while user.name have the new value
// I would update only its name while the login value should keep the value
// in the database
userRepository.save(user);
// ...
}
I know that I could load the user using findOne, then change its name and update it using save... But if I have 100 fields and I want to update 50 of them it could be very annoying change each value..
Is there no way to tell something like "skip all null values when save the object"?
I had the same question and as M. Deinum points out, the answer is no, you can't use save. The main problem being that Spring Data wouldn't know what to do with nulls. Is the null value not set or is it set because it needs to be deleted?
Now judging from you question, I assume you also had the same thought that I had, which was that save would allow me to avoid manually setting all the changed values.
So is it possible to avoid all the manuel mapping then? Well, if you choose to adhere to the convention that nulls always means 'not set' and you have the original model id, then yes.
You can avoid any mapping yourself by using Springs BeanUtils.
You could do the following:
Read the existing object
Use BeanUtils to copy values
Save the object
Now, Spring's BeanUtils actual doesn't support not copying null values, so it will overwrite any values not set with null on the exiting model object. Luckily, there is a solution here:
How to ignore null values using springframework BeanUtils copyProperties?
So putting it all together you would end up with something like this
#RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> updateUser(#RequestBody User user) {
User existing = userRepository.read(user.getId());
copyNonNullProperties(user, existing);
userRepository.save(existing);
// ...
}
public static void copyNonNullProperties(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
Using JPA you can do it this way.
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("lastSeen"), date);
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
You are able to write something like
#Modifying
#Query("update StudentXGroup iSxG set iSxG.deleteStatute = 1 where iSxG.groupId = ?1")
Integer deleteStudnetsFromDeltedGroup(Integer groupId);
Or If you want to update only the fields that were modified you can use annotation
#DynamicUpdate
Code example:
#Entity
#Table(name = "lesson", schema = "oma")
#Where(clause = "delete_statute = 0")
#DynamicUpdate
#SQLDelete(sql = "update oma.lesson set delete_statute = 1, "
+ "delete_date = CURRENT_TIMESTAMP, "
+ "delete_user = '#currentUser' "
+ "where lesson_id = ?")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
If you are reading request as JSON String, this could be done using Jackson API. Here is code below. Code compares an existing POJO Elements and create new one with updated fields. Use the new POJO to persist.
public class TestJacksonUpdate {
class Person implements Serializable {
private static final long serialVersionUID = -7207591780123645266L;
public String code = "1000";
public String firstNm = "John";
public String lastNm;
public Integer age;
public String comments = "Old Comments";
#Override
public String toString() {
return "Person [code=" + code + ", firstNm=" + firstNm + ", lastNm=" + lastNm + ", age=" + age
+ ", comments=" + comments + "]";
}
}
public static void main(String[] args) throws JsonProcessingException, IOException {
TestJacksonUpdate o = new TestJacksonUpdate();
String input = "{\"code\":\"1000\",\"lastNm\":\"Smith\",\"comments\":\"Jackson Update WOW\"}";
Person persist = o.new Person();
System.out.println("persist: " + persist);
ObjectMapper mapper = new ObjectMapper();
Person finalPerson = mapper.readerForUpdating(persist).readValue(input);
System.out.println("Final: " + finalPerson);
}}
Final output would be, Notice only lastNm and Comments are reflecting changes.
persist: Person [code=1000, firstNm=John, lastNm=null, age=null, comments=Old Comments]
Final: Person [code=1000, firstNm=John, lastNm=Smith, age=null, comments=Jackson Update WOW]
skip all null values when save the object
As others have pointed out, there is not straight forward solution in JPA.
But thinking out of the box you can use MapStruct for that.
This means you use the right find() method to get the object to update from the DB, overwrite only the non-null properties and then save the object.
You can use JPA as you know and just use MapStruct like this in Spring to update only the non-null properties of the object from the DB:
#Mapper(componentModel = "spring")
public interface HolidayDTOMapper {
/**
* Null values in the fields of the DTO will not be set as null in the target. They will be ignored instead.
*
* #return The target Holiday object
*/
#BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
Holiday updateWithNullAsNoChange(HolidayDTO holidayDTO, #MappingTarget Holiday holiday);
}
See the MapStruct docu on that for details.
You can inject the HolidayDTOMapper the same way you do it with other beans (#Autowired, Lombok,...) .
the problem is not spring data jpa related but the jpa implementation lib that you are using related.
In case of hibernate you may have a look at:
http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/
For long,int and other types;
you can use the following code;
if (srcValue == null|(src.getPropertyTypeDescriptor(pd.getName()).getType().equals(long.class) && srcValue.toString().equals("0")))
emptyNames.add(pd.getName());
Play framework 2 update() method acts weirdly in my project.
My project has following model classes:
#entity
public class Images{
#Id
public Long id;
#Lob
public byte[] imageFile;
}
#entity
public class Users {
#Id
public Long id;
public String name;
public Blobs photo;
}
This is index.scala.html:
#(user : Users)
<p>#user.id</p>
<p>#user.name</p>
<p>#user.photo.id</p>
edit user no. 1
And this is the code in my editUser.scala.html:
#(id : Long, userForm : Form[Users])
#form(action = routes.Application.update(id))
#inputText(userForm("name"))
#inputText(userForm("photo.id"))
<input type="submit" value="UPDATE">
Here's my controller class:
public class Application extends Controller {
public static Result index() {
return ok(index.render(Users.find.byId(1L)));
}
public static Result editUser(Long id) {
Form<Users> userForm = form(Users.class).fill(Users.find.byId(id));
return ok(
views.html.editUser.render(id, userForm)
);
}
public static Result update(Long id) {
Form<Users> userForm = form(Users.class).bindFromRequest();
userForm.get().update(id);
return redirect((routes.Application.index()));
}
}
Assume that there is one entry in Blobs class with id=1 and a blob and there is one entry in Users class also with id=1. Now I want to update that user and set the photo id from the Blobs table.
The problem is when I try to update user from editUser form and refer back to index.scala.html, I get a null pointer exception. It seems update() method just returns null for photo field in Users form, though it has no problem updating name value of the user. I checked with userForm.field("photo").value() and it seems that bindFromRequest works properly. But after updating, the photo field is just null. Does anyone know what the problem could be?
Edit:
P.S: This program will begin with a null pointer exception, because photo field of Users is null at the beginning of the program. Just assume we go directly to the edit page and we try to update Users with a photo from Blobs. The problem is even after the update, I get null pointer exception and it seems update doesn't affect that field in User class
For an object Customer, I have multiple orders. So, In one page, I have a customer and multiple order objects,
<select name="customer.order[0].orderType" id="orderType" >
#{list orderTypes}
<option value="${_.id}">${_.name}</option>
#{/list}
</select>
<select name="customer.order[1].orderType" id="orderType" >
#{list orderTypes}
<option value="${_.id}">${_.name}</option>
#{/list}
</select>
Model Customer:
public class Customer{
#OneToMany(mappedBy = "customer")
public List<Order> orders;
}
Model Order:
public class Order{
#ManyToOne
public TypeOrder orderType;
}
Controller :
public static void saveCustomerOrder(Customer customer) {
customer.save();
System.out.println(customer.orderType.name + " " + customer.order.size() + " " + customer.order.get(0).orderType);
}
So I get the the order size as 2 ; but i dont get the data for the orderType. Can anyone help me with this?
(It doesn't persist in the database, but customer object is saved)
When I do customer.save(); I would like customer to be saved and order to be saved;
(this is just a simple sample program)
--EDIT---
It works if I save the orders separately by looping through them.
for(Order o : customer.orders)
o.save();
I just want to know if it is possible to save the child model when i save the parent model. Am I missing some annotation?
The way Play 1.x works, as per documentation, you have to explicitly save every object you want to persist. So yes, you have to iterate over all the objects.
I am trying to do a form that has one model and 2 values that are always the same but there is one value that changes.
My html form is:
#helper.form(action = routes.TeacherController.putQuestionToAssignment(), 'class -> "form-horizontal") {
<input type="hidden" name="size" value="#{size}">
<input type="hidden" name="assignment_id" value="#{assignment_id}">
#for(question <- questions){
<input type="checkbox" name="question_id" value="#{question.question_id}">
}
<button type="submit" class="btn btn-primary" value="submit">create</button>
}
my model:
#Entity
public class Verkefnaspurningar extends Model {
#Id
public int verkefnaspurningar_id;
#Constraints.Required
public int assignment_id;
#Constraints.Required
public int question_id;
#Constraints.Required
public Double size;
and in the controller i am going to try to create a model for each question that is checked in the checkbox in the form, but i have no idea how to do that, can i maybe set an id on the question_id field in the form and loop through that?
public static Result putQuestionToAssignment(){
Form<Verkefnaspurningar> verkefnaspurningarForm = form(Verkefnaspurningar.class).bindFromRequest();
int question_id= verkefnaspurningarForm.get().question_id;
Double size= verkefnaspurningarForm.get().size;
int assignmentId= verkefnaspurningarForm.get().assignment_id;
Verkefnaspurningar verkefnaspurningar = Verkefnaspurningar.create(assignmentId, question_id, size);
return redirect(routes.TeacherController.createAssignment());
}
Hoping that this post is not to dumb, my first one, with kind regards Björn.
Here is my idea to the solution for your problem. But, I test this solution using Play!Framework 2.1.0.
In your controller, you can get the checked question_id value submitted by user like this :
public static Result putQuestionToAssignment() {
// get all HTTP request value from submitted form
Map<String, String[]> map = request().body().asFormUrlEncoded();
String[] allCheckedData = map.get("question_id"); // get all checked question
... // bind the rest of request parameter to variable if needed
Verkefnaspurningar verkefnaspurningar;
// Loop for each checked question
for (String t : allCheckedData) {
Logger.info("Checked data is " + t);
verkefnaspurningar = new Verkefnaspurningar(); // create each data
... // put the rest of code for saving the model
}
return TODO; // still dummy. Should be redirect!
}
Please note that I'm not use Form in this solution. I get all the request parameter from submitted form using request().body().asFormUrlEncoded(). Check documentation for RequestBody here.
I would use a DynamicForm for this. It would be something like:
public static Result putQuestionToAssignment(){
DynamicForm form = form().bindFromRequest();
Set<Long> ids = new HashSet<Long>();
for (Entry<String, String> entry : form.data().entrySet()) {
ids.add(Long.valueOf(entry.getValue()));
}
Verkefnaspurningar verkefnaspurningar;
for (Long id : ids) {
verkefnaspurningar = Verkefnaspurningar.create(assignmentId, id, ids.size());
}
return redirect(routes.TeacherController.createAssignment());
}
To get the assignmentId, you may have to add a condition when looping over form.data().entrySet(): if entry.getKey() if the one of your assignment, then store id, else add value to Set.