In my current spring project, my forms are implement with a structure like this:
<form class="form" id="Pagina" role="form" method="POST" action="/loja/Pagina/cadastra" enctype="multipart/form-data">
...
</form>
and it's processed in server by this methos:
controller
@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();
}
service
@PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
@Transactional
public E cadastra(E e, MultipartFile file, MultipartFile icone, MultipartFile[] screenshot) {
return dao.persist(e);
}
My problem it's when the form have a field like this:
<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>
which maps a atribute like this in the entiy class:
@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;
Where the options inside looks like this:
<option value="">.</option>
<option value="...">...</option>
If I submit the form with the blank option selected, I get this error:
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
but if, for instance, insert a record manually in the database (in my case, using pgAdmin3), and select this item in the select, the form is submitted without errors.
Anyone can tell me how I fix that, to allow me submit the form with or without selected data from the <select>
.
UPDATE
code for the class 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);
}
}
}
method added to my controller:
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Pagina.class, new PaginaEditor());
}
Selects are tricky ones in Spring MVC.
I think your problem is that when your main entity is getting to the data layer to be persisted, the relationship is not there.
Try to debug and check if affirmation above is true.
There are two ways to get this sorted.
Let's assume a Company / Contact relationship in a contacts system. A company has many contacts and a contact has one company.
Company snippet.
// 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
}
Contact snippet
// 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
}
And a jsp snippet with the select. We assume there is a "contact" object and a list of customers in the model.
<form:form modelAttribute="contact">
<form:input path="name" />
<form:select path="customer" items="${customers}" itemValue="id" itemLabel="name" />
</form:form>
With this code in place, you can use a PropertyEditor like this.
@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);
}
}
}
and register in your Spring Context like this.
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Customer.class, this.customerEditor);
}
What happens here is that, whenever Spring finds an Object of type Customer, it will use the PropertyEditor to convert the (in this case) Id to the object and by the type the contact (in this case) gets to the data layer (Hibernate), the proper Customer entity will be there waiting as happy as Larry.
That is automagic way to do it.
Another way to do it is to create a form/DTO or whatever you would like to call and add the fields including a customerId field (in this case) and convert your self before you save your entity.
I hope I understood your problem right because it took me quite a few minutes to write this... :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With