Announcement Announcement Module
Collapse
No announcement yet.
Hibernate Validator with @Cascade and editing data problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate Validator with @Cascade and editing data problem

    Perhaps it's a bug in Hibernate Validator, but cascade validation with the @Valid annotation does not seem to work if the domain objects are populated from a database. On submit, I get validation errors, even thought all the required fields have content. This strange behaviour drives me crazy and I have struggled with this issue in several weeks.

    Here's what I have included in my formBackingObject method:
    Code:
    @ModelAttribute("foo")
        public Foo formBackingObject(@RequestParam(value = "id", required = false) Long id) {
            if (id != null) {
                Foo foo = fooDAO.findById(id);
                LOG.info(foo);
                return foo;
            }
            return new Foo();
        }
    If I populate the domain object without a database, cascade validation works:
    Code:
    Foo foo = new Foo();
    Bar bar = new Bar();
    bar.setObligatory("some content");
    ...
    foo.setBar(bar);
    ...
    I have created a small application which can be downloaded and installed very easily if someone wants to check. It includes a Derby database which will be created automatically the first time the application is started.
    1. Download and unzip: http://folk.uio.no/erlendfg/validator/
    2. mvn jetty:run
    3. http://localhost/validator_test
    4. Follow the edit link and press the Save button.

    Have I missed something or should I report this as a Hibernate Validator bug?

    The contents of my domain objects and the controller follow for those of you who don't want to install the application. Be aware of the following annotation in the first domain object (Foo) which does not work properly:
    Code:
    @Valid
    private Bar bar;

    Code:
    package no.uio.webapps.domain;
    
    import java.io.Serializable;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    import javax.validation.Valid;
    
    import org.hibernate.validator.constraints.NotEmpty;
    
    @Entity
    @Table(name = "FOO")
    public class Foo implements Serializable {
    
        public Foo() {
        }
    
        private Long id;
        @NotEmpty
        private String obligatory;
        private String optional;
        @Valid
        private Bar bar;
    
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "foo_id", unique = true, nullable = false)
        public Long getId() {
            return id;
        }
    
    
        public void setId(Long id) {
            this.id = id;
        }
    
    
        @Column(name = "obligatory", nullable = false, length = 100)
        public String getObligatory() {
            return obligatory;
        }
    
    
        public void setObligatory(String obligatory) {
            this.obligatory = obligatory;
        }
    
    
        @Column(name = "optional", length = 100)
        public String getOptional() {
            return optional;
        }
    
    
        public void setOptional(String optional) {
            this.optional = optional;
        }
    
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "bar_id")
        public Bar getBar() {
            return bar;
        }
    
    
        public void setBar(Bar bar) {
            this.bar = bar;
        }
    
    
        @Override
        public String toString() {
            return id + ", " + obligatory + ", " + optional + ", " + bar.toString();
        }
    }
    Code:
    package no.uio.webapps.domain;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    import org.hibernate.validator.constraints.NotEmpty;
    
    @Entity
    @Table(name = "BAR")
    public class Bar implements Serializable {
    
        public Bar() {
        }
    
        private Long id;
        @NotEmpty
        private String obligatory;
        private String optional;
        private Set<Foo> foos = new HashSet<Foo>(0);
    
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "bar_id", unique = true, nullable = false)
        public Long getId() {
            return id;
        }
    
    
        public void setId(Long id) {
            this.id = id;
        }
    
    
        @Column(name = "obligatory", nullable = false, length = 100)
        public String getObligatory() {
            return obligatory;
        }
    
    
        public void setObligatory(String obligatory) {
            this.obligatory = obligatory;
        }
    
    
        @Column(name = "optional", length = 100)
        public String getOptional() {
            return optional;
        }
    
    
        public void setOptional(String optional) {
            this.optional = optional;
        }
    
    
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "bar")
        public Set<Foo> getFoos() {
            return foos;
        }
    
    
        public void setFoos(Set<Foo> foos) {
            this.foos = foos;
        }
    
    
        @Override
        public String toString() {
            return id + ", " + obligatory + ", " + optional;
        }
    
    }
    Code:
    package no.uio.webapps.web;
    
    import java.util.List;
    
    import javax.validation.Valid;
    
    import no.uio.webapps.dao.BarDAO;
    import no.uio.webapps.dao.FooDAO;
    import no.uio.webapps.domain.Foo;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.transaction.annotation.Transactional;
    import org.springframework.ui.ModelMap;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class FooController {
    
        private FooDAO fooDAO;
        private BarDAO barDAO;
        private static final Logger LOG = Logger.getLogger(Foo.class);
    
    
        @ModelAttribute("foo")
        public Foo formBackingObject(@RequestParam(value = "id", required = false) Long id) {
            if (id != null) {
                Foo foo = fooDAO.findById(id);
                LOG.info(foo);
                return foo;
            }
            return new Foo();
        }
    
    
        @RequestMapping(value = "/index.html", method = RequestMethod.GET)
        public ModelMap index(ModelMap modelMap) {
            List<Foo> foos = fooDAO.findAll();
            modelMap.addAttribute("foos", foos);
            return modelMap;
        }
    
    
        @RequestMapping(value = "/foo.html", method = RequestMethod.GET)
        public void show() {
        }
    
    
        @Transactional
        @RequestMapping(value = "/foo.html", method = RequestMethod.POST)
        public String submit(@ModelAttribute("foo") @Valid Foo foo, BindingResult result) {
    
            LOG.info(foo);
    
            if (result.hasErrors()) {
                LOG.error("We have validation errors: " + result.getAllErrors());
                return "/foo";
            } else {
                LOG.info("Validation passed without errors");
            }
    
            if (foo.getId() != null) {
                LOG.info("Updating data ...");
                barDAO.flush();
                fooDAO.flush();
            }
    
            LOG.info("Saving data ...");
            fooDAO.persist(foo);
            barDAO.persist(foo.getBar());
    
            return "/result";
        }
    
    
        @Autowired
        public void setFooDAO(FooDAO fooDAO) {
            this.fooDAO = fooDAO;
        }
    
    
        @Autowired
        public void setBarDAO(BarDAO barDAO) {
            this.barDAO = barDAO;
        }
    
    }
    Last edited by erlendfg; Jun 24th, 2010, 09:52 AM.

  • #2
    Hi,

    I had a similar problem with hibernate validation, where the validation didn't cascade to a lazy fetched object coming from the db despite of marking it with @Valid. I then made the relation eager by replacing the annotation @ManyToOne(fetch = FetchType.LAZY)
    by @ManyToOne(fetch = FetchType.EAGER) and it worked.

    Cheers, Frank

    Comment

    Working...
    X