Update Spring Data JPA

0

Witam, piszę prostego CRUDa z wykorzystaniem Spring Data JPA, mam mały problem, z akcją update. Rozszerzam swoje repozytorium za pomocą klasy

 JpaRepository<Person, Long>

Controller wygląda tak :


        @RequestMapping(value ="/update/{personId}" , method = RequestMethod.GET)
	public String updatePerson(@PathVariable ("personId") long id, Model model ){
		
		model.addAttribute("personFromModel", personRepository.findOne(id));
		
		
		return "updatePerson";
	}

	@RequestMapping(value ="/update/{personId}", method= RequestMethod.POST)
	public String processingUpdatingPersonInfoByAdmin(@ModelAttribute("personFromModel") Person person){
		
		
		personRepository.save(person);
		
		return "redirect:/App/person/showAll-admin";
	}

Problem : za każdym razem zostaję tworzona nowa osoba ? Dlaczego, przecież w metodzie GET znajduję osobę, którą dodaje do modelu, która następnie jest przekazywana do metody POST. Dodam tylko, iż wyczytałem, że zarówno create/update uzyskuję się za pomocą metody save().

Pozdrawiam

0

Metoda

updatePerson

zwraca użytkownika na widok z jakimś formularzem, z którego sobie dowolnie edytujesz dane. Dobrze rozumiem? Czy na tym widoku masz w jakimś inpucie, najlepiej ukrytym id tego użytkownika? Jeśli nie to potem w metodzie processingUpdatingPersonInfoByAdmin

 za pomocą modelAttribute przekazujesz obiekt typu Person zawierający tylko dane z formularza. Jeśli nie było tam id to w metodzie save ląduje obiekt person bez id i w konsekwencji tworzony jest nowy wpis w bazie.
0

@olek1

tak wygląda część kodu z mojego widoku :

		
                        <s:form commandName="personFromModel" method="POST">
			<s:hidden path="id" value="${personFromModel.id}" />              <--- dodałem, ale wciąż to samo
			<fieldset class="form-group">
				<s:label path="firstName">First Name : </s:label>
				<s:input class="form-control" path="firstName" type="text"
					required="required"></s:input>
			</fieldset>

			<fieldset class="form-group">
				<s:label path="lastName">Last Name : </s:label>
				<s:input class="form-control" path="lastName" type="text"
					required="required"></s:input>
			</fieldset>
			<button class="btn btn-info" type="submit">Update</button>

		</s:form>

Dodam tylko, że skrót s: to :

<%@taglib uri="http://www.springframework.org/tags/form" prefix="s"%>
0

To value chyba nie jest potrzebne.

value="${personFromModel.id}"

Pokaż może klasę Person jak wygląda oraz zaloguj sobie jaki obiekt person dostajesz przed wykonaniem: personRepository.save(person);

0

@olek1


@Entity
public class Person {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;
	
	@Size(min = 3, max = 20)
	private String firstName;
	
	@Size(min=3, max = 20)
	private String lastName;
	
	@OneToMany(mappedBy="person")
	private Set<Animal> setOfAnimals = new HashSet<>();

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public long getId() {
		return id;
	}

	
	public Set<Animal> getSetOfAnimals() {
		return setOfAnimals;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
		result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (firstName == null) {
			if (other.firstName != null)
				return false;
		} else if (!firstName.equals(other.firstName))
			return false;
		if (lastName == null) {
			if (other.lastName != null)
				return false;
		} else if (!lastName.equals(other.lastName))
			return false;
		return true;
	}
	
	
	
}

0

Daj jeszcze logi z tego jaki obiekt ląduje przed wywołaniem:

personRepository.save(person);

jakie ma id i inne pola.
Lepiej jako id używać typu obiektowego Long, zamiast long. Ponieważ long od początku przyjmuje wartość 0, co może być niepożądane przy automatycznej generacji id.

Ogólnie domyślam się, że twój widok wygląda tak, że masz tabelkę z użytkownikami, klikasz na jakiegoś edytuj i przekierowuje Cię do formularza, który zawiera już dane wcześniej wybranego użytkownika i go sobie dowolnie edytujesz, a następnie zapisujesz zmiany. Dobrze się domyślam? Czy dane w tym formularzu są wypełnione? Bo jeśli nie to może złe id przekazujesz i problem jest już w metodzie updatePerson.

0

@olek1

No i rzeczywiście
przed wywołaniem :

hash code = 1549105843
id = 0

po wywołaniu :
hash code = 1549105843
id = 39 (czyli tworzymy nowy obiekt w bazie)

Mógłbyś mi pomóc to naprawić ? Próbowałem zmienić typ na Long obiektowy, ale dalej to samo z tym, że w logu dostaje nulla ( a nie 0), czy aby dobrze ustawiam to id za pomocą

<s:hidden  path="id" />

, bo przyznam, że już nie wiem jak się do tego dobrać

0

Pokaż widok, z którego wywoływana jest metoda

updatePerson

i zaloguj w tej metodzie co dostajesz jako argument: @PathVariable ("personId") long id

 Przypuszczam, że dostajesz złe id z widoku do metody.
0

@olek1

Problem rozwiązałem dodając edytowanego osobę jako atrybut sesji, którą w metodzie POST za pomocą metody status.setComplete() czyszczę, czy jest to dobre rozwiązanie Twoim zdaniem ?

zalogowałem tak jak radziłeś id, i dostaję poprawne.

A oto widok :

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="s"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Update person nr. ${person.id}</title>
<link rel="stylesheet"
	href="/webjars/bootstrap/3.3.6/css/bootstrap.min.css">
</head>


<body>
	<div class="container">
		<h1>Update ${person.firstName}
			${person.lastName}</h1>
		<s:form commandName="person" method="POST">         
			<fieldset class="form-group">
				<s:label path="firstName">First Name : </s:label>
				<s:input class="form-control" path="firstName" type="text"
					required="required"></s:input>
			</fieldset>

			<fieldset class="form-group">
				<s:label path="lastName">Last Name : </s:label>
				<s:input class="form-control" path="lastName" type="text"
					required="required"></s:input>
			</fieldset>
			<button class="btn btn-info" type="submit">Update</button>

		</s:form>


	</div>
	<script src="/webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
	<script src="/webjars/jquery/2.2.3/dist/jquery.min.js"></script>

</body>
</html>
0

Moim zdaniem, pomysł z wrzucaniem tego do sesji nie jest najlepszy.

To jest twój jedyny widok? Wskaż mi miejsca, z których wiadomo, kiedy ma zostać wywołana metoda kontrolera:

@RequestMapping(value ="/update/{personId}" , method = RequestMethod.GET) 

a kiedy

@RequestMapping(value ="/update/{personId}", method= RequestMethod.POST) 

W twoim formie

<s:form commandName="person" method="POST"> 

brakuje atrybutu action, który powinien wyglądać mniej więcej tak:

action="${rc.getContextPath()}/update/${person.id}"

Gdzie rc to requestContext, z którego odwołujesz się do kontekstu aplikacji. Jak go zdefiniować, to masz odpowiedź tutaj: http://stackoverflow.com/questions/6970115/spring-mvc-request-urls-in-jsp

przez dodanie tego wiadomo, którą metodę kontrolera należy wykonać. Poza tym akurat w przesyłaniu formularza postem ten personId nie jest Ci już zupełnie do niczego potrzebny. Nie używasz w niej już id, gdyż przekazujesz tam cały model przez

@ModelAttribute

Zobacz sobie może takiego przykładowego cruda, wpisując Spring MVC Crud i zobacz jak tam jest to zrobione.
Pierwszy lepszy przykład: http://www.journaldev.com/3531/spring-mvc-hibernate-mysql-integration-crud-example-tutorial

0

Nie, mam odwołanie z innego widoku, jest to przycisk i link zarazem wygląda to tak (w tym widoku pokazuję listę wszystkich pracowników) :

<c:forEach items="${allPersons}" var="person">
					<tr>
						<td>${person.firstName}</td>
						<td>${person.lastName}</td>
						<td><a class ="btn btn-info" href="/Company/person/update/${author.id}">Update</a></td>
						<td><a class ="btn btn-danger" href="/Company/person/delete/${author.id}">Delete</a></td>
					</tr>
				</c:forEach>

Potem następuję przekierowanie na stronę edytującą. Myślałem, że jeżeli dodam w kolejnym widoku

 <s:form commandName="person" method="POST"> 

to zadziała

Kombinowałem coś wcześniej z tym atrybutem action, ale spróbuje raz jeszcze.

Wielkie dzięki za cierpliwość ;)

0

@up
Nie mogę edytować a mam tam byka.

<c:forEach items="${allPersons}" var="person">

${person.firstName}</td>
${person.lastName}</td>
Update</td>
Delete</td>
</tr>
</c:forEach>

1 użytkowników online, w tym zalogowanych: 0, gości: 1