Announcement Announcement Module
Collapse
No announcement yet.
Object binding problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Object binding problem

    Hello,
    I've got some unknown problem with binding value from Spring form select to object referencing this value. Object is Relation class and it has reference to Pet class object in it. When I try to add relation according to created form, Java returns following error:

    Code:
    org.hibernate.PropertyValueException: not-null property references a null or transient value: org.springframework.samples.petclinic.Relation.pet2
    I'll show some code fragments:

    relationForm.jsp
    Code:
    <form:form modelAttribute="relation">
    	<form:select path="pet2" items="${pets}" multiple="false" />
    	<form:checkbox path="positive" />
    	<input type="hidden" name="pet" value="${relation.pet.id}"/>
    	<p class="submit"><input type="submit" value="Add Relation" /></p>
    </form:form>
    addRelationForm.java
    Code:
    package org.springframework.samples.petclinic.web;
    
    // imports
    
    @Controller
    @RequestMapping("/owners/*/pets/{petId}/relations/new")
    @SessionAttributes("relation")
    public class AddRelationForm {
    
    	private final Clinic clinic;
    
    	@Autowired
    	public AddRelationForm(Clinic clinic) {
    		this.clinic = clinic;
    	}
    
    	@InitBinder
    	public void setAllowedFields(WebDataBinder dataBinder) {
    		dataBinder.setDisallowedFields("id");
    	}
    
    	@RequestMapping(method = RequestMethod.GET)
    	public String setupForm(@PathVariable("petId") int petId, Model model) {
    		Pet pet = this.clinic.loadPet(petId);
    		List<Relation> relations=pet.getRelations();
    		Collection<Pet> pets=this.clinic.getPets();
    		pets.remove(pet);
    		for(int i=0; i<relations.size(); i++){
    			pets.remove(relations.get(i).getPet2());
    		}
    		Relation relation = new Relation();
    		relation.setPet(pet);
    		pet.addRelation(relation);
    		model.addAttribute("relation", relation);
    		model.addAttribute("pets", pets);
    		return "pets/relationForm";
    	}
    
    	@RequestMapping(method = RequestMethod.POST)
    	public String processSubmit(@ModelAttribute("relation") Relation relation,
    			BindingResult result, SessionStatus status) {
    		new RelationValidator().validate(relation, result);
    		this.clinic.storeRelation(relation);
    		return "redirect:/owners/" + relation.getPet().getOwner().getId();
    	}
    }
    Hibernate mapping file
    Code:
    <class name="Pet" table="pets">
    		<id name="id" column="id">
    			<generator class="identity" />
    		</id>
    		<property name="name" column="name" />
    		<property name="birthDate" column="birth_date" type="date" />
    		<many-to-one name="owner" column="owner_id"
    			class="org.springframework.samples.petclinic.Owner" />
    		<many-to-one name="type" column="type_id"
    			class="org.springframework.samples.petclinic.PetType" />
    		<set name="visitsInternal" inverse="true" cascade="all" lazy="false">
    			<key column="pet_id" />
    			<one-to-many class="org.springframework.samples.petclinic.Visit" />
    		</set>
    		<set name="relationsInternal" inverse="false" cascade="all" lazy="false">
    			<key column="pet_id"/>
    			<one-to-many class="org.springframework.samples.petclinic.Relation"/>
    		</set> 
    	</class>
    
    <class name="Relation" table="relations">
    		<id name="id" column="id">
    			<generator class="identity" />
    		</id>
    		<property name="positive" column="positive" type="boolean" />
    		<many-to-one name="pet" column="pet_id"
    			class="org.springframework.samples.petclinic.Pet" />
    		<many-to-one name="pet2" class="org.springframework.samples.petclinic.Pet"
    			column="pet2_id" not-null="true" cascade="all" unique="true"/>
    	</class>
    I don't see any errors here, what is wrong?

  • #2
    The problem is that you are setting the pet on your relation object but you are not setting the pet2 object on your relation object and your mapping states that pet2 is not nullable.

    You either need to set the pet2 object when you create the Relation object or change your mapping file.

    Comment


    • #3
      I need to choose pet2 from form. It cannot be nullable. I am real beginner and I don't know, where I made a mistake, which makes pet2 null and not value selected from select field in form.

      Comment


      • #4
        You are close then. What version of Spring are you using? can you post your view code that has your form in it? Also are you using Property editors or the Conversion service?

        Comment


        • #5
          I am using the latest version of Spring (if I remember correctly: 3.0.3). About view... you mind .jsp? I'll put whole source of file with form and files included:

          relationForm.jsp
          Code:
          <%@ include file="/WEB-INF/jsp/includes.jsp"%>
          <%@ include file="/WEB-INF/jsp/header.jsp"%>
          
          <form:form modelAttribute="relation">
          	<form:select path="pet2" items="${pets}" multiple="false" />
          	<form:checkbox path="positive" />
          	<input type="hidden" name="pet" value="${relation.pet.id}"/>
          	<p class="submit"><input type="submit" value="Add Relation" /></p>
          </form:form>
          <br />
          
          <b>Relations with other pets:</b>
          <table width="333">
          	<tr>
          		<th>Name</th>
          		<th>Relation</th>
          	</tr>
          	<c:forEach var="relationx" items="${relation.pet.relations}">
          
          		<c:if test="${relationx.pet2.name!=null}">
          			<tr>
          				<td>${relationx.pet2}</td>
          				<c:if test="${relationx.positive}">
          					<td class="positive">Positive</td>
          				</c:if>
          				<c:if test="${!relationx.positive}">
          					<td class="negative">Negative</td>
          				</c:if>
          			</tr>
          		</c:if>
          
          	</c:forEach>
          </table>
          
          <%@ include file="/WEB-INF/jsp/footer.jsp"%>
          includes.jsp
          Code:
          <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
          <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
          <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>\
          header.jsp and footer.jsp (I don't think that they are needed here, but let's have whole view):
          Code:
          <!--
          	PetClinic :: a Spring Framework demonstration
          -->
          <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
          
          <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <link rel="stylesheet" href="<spring:url value="/static/styles/petclinic.css" htmlEscape="true" />" type="text/css"/>
            <title>PetClinic :: a Spring Framework demonstration</title>	
          </head>
          
          <body>
          
            <div id="main">
          Code:
            <table class="footer">
              <tr>
                <td><a href="<spring:url value="/" htmlEscape="true" />">Home</a></td>
                <td align="right"><img src="<spring:url value="/static/images/springsource-logo.png" htmlEscape="true" />" alt="Sponsored by SpringSource"/></td>
              </tr>
            </table>
          
            </div>
          </body>
          
          </html>
          //edit:
          I forgot, I am not using any of them both.
          Last edited by IceMan; Jul 19th, 2010, 04:30 PM.

          Comment


          • #6
            ok, I would set the itemLabel and the itemValue attributes on the <form:select tag. This tells spring what to display as the label and what to use as the value for the <option> markup that it will generate. In your case it looks like you would set the itemLabel to "name" and the itemValue to "id".

            Finally, you need to setup the ConversionService or a PropertyEditor so that Spring knows how to translate the "id" value of the option markup to the Pet entity.

            I use the ConversionService in most of my apps but you can use either one.

            Comment


            • #7
              I had itemLabel and itemValue set before, but according to what you write here, these weren't only things missing. I haven't heard about ConversionService or PropertyEditor, tutorials never said anything about them. I'll try tomorrow and hope it will help. Thanks in advance!

              Comment


              • #8
                Take a look at this link. It should help you out.

                Comment


                • #9
                  I don't understand all behind the scene things about ConversionService. I have added following bean to my context.xml file:
                  Code:
                  <bean id="conversionService"
                  		class="org.springframework.context.support.ConversionServiceFactoryBean">
                  		<property name="converters">
                  			<list>
                  				<bean class="org.springframework.samples.petclinic.PetConverter" />
                  			</list>
                  		</property>
                  	</bean>
                  and created class PetConverter.java:
                  Code:
                  package org.springframework.samples.petclinic;
                  
                  import java.util.ArrayList;
                  
                  import org.springframework.beans.factory.annotation.Autowired;
                  import org.springframework.core.convert.converter.Converter;
                  
                  public class PetConverter implements Converter<String,Pet>{
                  
                  	@Autowired
                  	Clinic clinic;
                  	
                  	public Pet convert(String arg0) {
                  		ArrayList<Pet> pets= new ArrayList<Pet>(clinic.getPets());
                  		for(Pet pet:pets){
                  			if(pet.getId().equals(Integer.getInteger(arg0))) return pet;
                  		}
                  		return null;
                  	}
                  }
                  What am I doing wrong and what should I do to make it all work?

                  Comment


                  • #10
                    I would run the debugger to see if your converter is truly being invoked and see if the problem is with your converter or with the setup of the conversion service.

                    Comment


                    • #11
                      Are you using:

                      Code:
                      <mvc:annotation-driven/>
                      inside your context.xml?

                      If so then you need to use

                      Code:
                      <mvc:annotation-driven conversion-service="conversionService" />

                      Comment


                      • #12
                        I have added this line into xml, in debug converter is still not invoked.

                        Comment


                        • #13
                          Can you post your full context.xml?

                          Comment


                          • #14
                            context.xml
                            Code:
                            <?xml version="1.0" encoding="UTF-8"?>
                            <!-- Application context definition for PetClinic on Hibernate. -->
                            <beans xmlns="http://www.springframework.org/schema/beans"
                            	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
                            	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
                            	xmlns:mvc="http://www.springframework.org/schema/mvc"
                            	xsi:schemaLocation="
                            			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                            			http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
                            
                            
                            	<!-- ========================= RESOURCE DEFINITIONS ========================= -->
                            
                            	<!-- import the dataSource definition -->
                            	<import resource="applicationContext-dataSource.xml" />
                            
                            	<!-- Configurer that replaces ${...} placeholders with values from a properties 
                            		file -->
                            	<!-- (in this case, Hibernate-related settings for the sessionFactory definition 
                            		below) -->
                            	<context:property-placeholder location="classpath:jdbc.properties" />
                            
                            	<!-- Hibernate SessionFactory -->
                            	<bean id="sessionFactory"
                            		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
                            		p:dataSource-ref="dataSource" p:mappingResources="petclinic.hbm.xml">
                            		<property name="hibernateProperties">
                            			<props>
                            				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
                            				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                            				<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
                            			</props>
                            		</property>
                            		<property name="eventListeners">
                            			<map>
                            				<entry key="merge">
                            					<bean
                            						class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
                            				</entry>
                            			</map>
                            		</property>
                            	</bean>
                            
                            	<!-- Transaction manager for a single Hibernate SessionFactory (alternative 
                            		to JTA) -->
                            	<bean id="transactionManager"
                            		class="org.springframework.orm.hibernate3.HibernateTransactionManager"
                            		p:sessionFactory-ref="sessionFactory" />
                            
                            	<!-- Transaction manager that delegates to JTA (for a transactional JNDI 
                            		DataSource) -->
                            	<!-- <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> -->
                            
                            
                            	<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
                            
                            	<!-- Activates various annotations to be detected in bean classes: Spring's 
                            		@Required and @Autowired, as well as JSR 250's @Resource. -->
                            	<context:annotation-config />
                            
                            	<!-- Instruct Spring to perform declarative transaction management automatically 
                            		on annotated classes. -->
                            	<tx:annotation-driven />
                            
                            	<!-- Exporter that exposes the Hibernate statistics service via JMX. Autodetects 
                            		the service MBean, using its bean name as JMX object name. -->
                            	<context:mbean-export />
                            
                            	<!-- PetClinic's central data access object: Hibernate implementation -->
                            	<bean id="clinic"
                            		class="org.springframework.samples.petclinic.hibernate.HibernateClinic" />
                            
                            	<!-- Hibernate's JMX statistics service -->
                            	<bean name="petclinic:type=HibernateStatistics" class="org.hibernate.jmx.StatisticsService"
                            		autowire="byName" />
                            		<mvc:annotation-driven/>
                            		<mvc:annotation-driven conversion-service="conversionService" />
                            	<bean id="conversionService"
                            		class="org.springframework.context.support.ConversionServiceFactoryBean">
                            		<property name="converters">
                            			<list>
                            				<bean class="org.springframework.samples.petclinic.PetConverter" />
                            			</list>
                            		</property>
                            	</bean>
                            </beans>

                            Comment


                            • #15
                              Are you also using a dispatcher-servlet.xml or just the context.xml?

                              Comment

                              Working...
                              X