2014-09-05 17 views
7

W moim bieżącego projektu wiosny, moje postacie są wdrożyć o strukturze jak ta:błąd gdy postać z wybranej dziedziny poddaje

<form class="form" id="Pagina" role="form" method="POST" action="/loja/Pagina/cadastra" enctype="multipart/form-data"> 
... 
</form> 

i jest przetwarzany na serwerze przez ten Methos:

kontroler

@RequestMapping(value="cadastra", method=RequestMethod.POST) 
@ResponseBody 
@ResponseStatus(HttpStatus.CREATED) 
public E cadastra(@ModelAttribute("object") E object, BindingResult result, @RequestParam(value="file", required=false) MultipartFile file, @RequestParam(value="icone", required=false) MultipartFile icone, @RequestParam(value="screenshot", required=false) MultipartFile screenshot[]) throws Exception { 
    E ret = serv.cadastra(object, file, icone, screenshot); 
    if (ret != null) 
     return ret; 
    else 
     throw new Exception(); 
} 

serwis

@PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)") 
@Transactional 
public E cadastra(E e, MultipartFile file, MultipartFile icone, MultipartFile[] screenshot) { 
    return dao.persist(e); 
} 

Moim problemem jest to, gdy postać ma pole takiego:

<label>pagina</label> 
<select name="pagina.id" class="form-control select" data-lista="/loja/Pagina/listagem.json"> 
... 
</select> 

<label>produto</label> 
<select name="produto.id" class="form-control select" data-lista="/loja/Produto/listagem.json"> 
... 
</select> 

który mapuje atribute jak to w klasie entiy:

@OneToOne(fetch = FetchType.EAGER) 
@JoinColumn(name="pagina_mae", nullable = true) 
@Order(value=5) 
@Select(name="pagina", ordem = 5) 
@Sidebar 
private Pagina pagina; 

@OneToOne(fetch = FetchType.EAGER) 
@JoinColumn(name="produto_mae", nullable = true) 
@Order(value=6) 
@Select(name="produto", ordem = 6) 
@Sidebar 
private Produto produto; 

Gdzie opcje wewnątrz wygląda tak :

<option value="">.</option> 
<option value="...">...</option> 

Po przesłaniu formularza z wybraną opcją pustą, Otrzymuję ten błąd:

object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina 

ale jeśli, na przykład, wstawić rekord ręcznie w bazie danych (w moim przypadku, używając pgAdmin3) i wybierz ten element w select, formularz jest przesyłany bez błędów.

Każdy może mi powiedzieć, jak to naprawić, aby umożliwić mi przesłanie formularza z lub bez wybranych danych z <select>.

UPDATE kod

dla klasy Pagina:

@Entity 
@Table(name="pagina") 
@MainForm(grupo = 2, icone = "file") 
public class Pagina extends ModelEntity { 

    @Id 
    @Column(name = "id") 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Integer id; 

    @Column(name = "nome", unique = true) 
    @Order(value=1) 
    @Input(type="hidden", name="nome", ordem = 1) 
    private String nome; 

    @Column(name = "titulo", nullable = false) 
    @Order(value=2) 
    @Input(name="titulo", ordem = 2) 
    private String titulo; 

    @Column(name = "descricao", length=65535) 
    @Order(value=4) 
    @Textarea(name="descricao", ordem = 4) 
    private String descricao; 

    @OneToOne(fetch = FetchType.EAGER) 
    @JoinColumn(name="pagina_mae", nullable = true) 
    @Order(value=5) 
    @Select(name="pagina", ordem = 5) 
    @Sidebar 
    private Pagina pagina; 

    @OneToOne(fetch = FetchType.EAGER) 
    @JoinColumn(name="produto_mae", nullable = true) 
    @Order(value=6) 
    @Select(name="produto", ordem = 6) 
    @Sidebar 
    private Produto produto; 
} 

UPDATE 2

PaginaEditor.java

@Component 
public class PaginaEditor extends PropertyEditorSupport { 

    @Inject 
    private PaginaService paginaService; 

    @Override 
    public void setAsText(String text) { 
     if (!text.isEmpty()) { 
      Pagina pagina = paginaService.getObject(text); 
      setValue(pagina); 
     } 
    } 

} 

Sposób dodano do mojego kontrolera:

@InitBinder 
public void initBinder(WebDataBinder binder) { 
    binder.registerCustomEditor(Pagina.class, new PaginaEditor()); 
} 
+0

to jest problem z ORM. Zgadywanie hibernacji? Jeśli dodasz tag do ORM, którego używasz, znacznie łatwiej znajdziesz jakąś pomoc. Sprawdź również to pytanie/awnser http://stackoverflow.com/questions/2302802/object-references-an-unsaved-transient-instance-save-the-transient-instance-be –

+0

czy możesz pokazać nam kod dao. persist (e); jak utrzymujesz się dokładnie, ponieważ wydaje mi się, że wiem, jaki jest tutaj problem. – Aeseir

+0

@ug_ Widziałem link, ale jeśli spróbuję podążać za sugestią (najpierw użyję opcji 'kaskada'), aplikacja spróbuj wstawić nową Paginę lub Produto i skojarzyć z Paginą, którą wstawiam. –

Odpowiedz

5

Wybór jest trudny w Spring MVC.

Myślę, że twój problem polega na tym, że kiedy twoja główna jednostka dostaje się do warstwy danych, która ma zostać utrwalona, ​​nie ma tam relacji.

Spróbuj przeprowadzić debugowanie i sprawdź, czy powyższe potwierdzenie jest prawdziwe.

Istnieją dwa sposoby na uzyskanie tego posortowania.

Załóżmy relację firma/kontakt w systemie kontaktów. Firma ma wiele kontaktów, a kontakt ma jedna firma.

Fragment firmy.

// package declaration imports and all 
@Entity 
@Table(name = "company") 
public class Company { 

    private String name; 

    @OneToMany(mappedBy = "company") 
    private List<Contact> contacts = new ArrayList<Contact>(); 

    // getter and setters and any extra stuff you fancy putting here 

} 

Kontakt fragment

// package declaration imports and all 
@Entity 
@Table(name = "contact") 
public class Contact { 

    private String name; 

    @ManyToOne 
    private Company company; 

    // getter and setters and any extra stuff you fancy putting here 

} 

I urywek JSP z select. Zakładamy, że w modelu znajduje się obiekt "kontakt" i lista klientów.

<form:form modelAttribute="contact"> 
    <form:input path="name" /> 
    <form:select path="customer" items="${customers}" itemValue="id" itemLabel="name" /> 
</form:form> 

Za pomocą tego kodu można użyć programu PropertyEditor w ten sposób.

@Component 
public class CustomerEditor extends PropertyEditorSupport { 

    @Inject 
    private CustomerService customerService; 

    @Override 
    public void setAsText(String text) { 
     if (StringUtils.isNotBlank(text)) { 
      Customer customer = this.customerService.findById(Integer 
        .valueOf(text)); 
      this.setValue(customer); 
     } 
    } 

} 

i zarejestruj się w swoim wiosennym kontekście takim jak to.

@InitBinder 
public void initBinder(WebDataBinder binder) { 
    binder.registerCustomEditor(Customer.class, this.customerEditor); 
} 

Co się dzieje tutaj jest to, że kiedy wiosną znajdzie obiekt typu klient będzie korzystał z PropertyEditor do konwersji (w tym przypadku) Id do obiektu i przez rodzaj kontaktu (w tym przypadku) dostaje się do warstwy danych (Hibernacja), właściwa jednostka Klienta będzie tam czekała tak szczęśliwa jak Larry.

To jest automagiczny sposób na zrobienie tego.

Innym sposobem, aby to zrobić, jest utworzenie formularza/DTO lub cokolwiek chcesz wywołać i dodanie pól, w tym pola CustomerId (w tym przypadku) i przekonwertowanie siebie przed zapisaniem jednostki.

Mam nadzieję, że zrozumiałem twój problem, ponieważ zajęło mi to kilka minut, aby napisać to ... :)

+0

Myślę, że to zadziała –

+0

Ok, rozumiem podstawy dotyczące twojej odpowiedzi, ale mam kilka pytań: 1) jaka jest biblioteka komponentu StringUtils? Kiedy dodaję to do mojej aplikacji, zaćmienie daje kilka opcji. 2) Gdzie powinienem umieścić klasę pochodną 'PropertyEditorSupport'? 3) Gdzie powinienem umieścić metodę "initBinder"? –

+1

@KleberMota StringUtils jest z apache commons tj. Https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html –

Powiązane problemy