Hibernate不保存已编辑的实体,同时保存新的实体

时间:2013-01-13 13:27:53

标签: spring hibernate spring-mvc testng

我面临一个奇怪的问题。我目前正在编写一个基于Spring-MVC 3.2和Hibernate 4.1.9的Web应用程序。我用TestNG单元测试编写了一个样本控制器,除编辑外,一切都很好。我可以看到,在保存一个新对象时,它就像一个魅力,但如果我尝试编辑一个现有的对象,它就不会在没有给出任何理由的情况下保存(我正在调用相同的方法来添加和更新)

添加Application

类型的新对象的日志
14:26:36.636 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing POST request for [/app/add.json]
14:26:36.637 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /app/add.json
14:26:36.650 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public java.lang.Long com.wstars.kinzhunt.platform.apps.web.AppController.createApp(com.wstars.kinzhunt.platform.model.apps.Application)]
14:26:36.651 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'appController'
14:26:36.821 [main] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Reading [class com.wstars.kinzhunt.platform.model.apps.Application] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@5dc6bb75]
14:26:36.890 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
14:26:36.890 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13580799968
14:26:36.892 [main] DEBUG c.w.c.dao.hibernate.BaseDaoHibernate - Saving or Updating Object: com.wstars.kinzhunt.platform.model.apps.Application@54fc519b[id=<null>,name=KinzHunt,company=com.wstars.kinzhunt.platform.model.apps.Company@151c2b4[id=1,name=KinzHunt],callbackUrl=http://www.kinzhunt.com/callback/,website=http://www.kinzhunt.com,senderEmail=no-reply@kinzhunt.com,logoUrl=<null>]
14:26:36.892 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
14:26:36.893 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13580799968
14:26:36.893 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
14:26:36.894 [main] DEBUG o.h.e.def.AbstractSaveEventListener - executing identity-insert immediately
14:26:36.898 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
14:26:36.899 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
14:26:36.899 [main] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:h2:mem:platform_test;DB_CLOSE_DELAY=-1]
14:26:36.901 [main] DEBUG org.hibernate.SQL - /* insert com.wstars.kinzhunt.platform.model.apps.Application */ insert into applications (id, callback_url, company_id, logo_url, name, sender_email, website) values (null, ?, ?, ?, ?, ?, ?)
14:26:36.904 [main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 2
14:26:36.904 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
14:26:36.905 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
14:26:36.905 [main] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
14:26:36.905 [main] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
14:26:36.926 [main] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Written [2] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@5dc6bb75]
14:26:36.927 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
14:26:36.928 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request

保存已编辑对象的日志为

14:27:03.398 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing POST request for [/app/1/edit.json]
14:27:03.398 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /app/1/edit.json
14:27:03.401 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public java.lang.Long com.wstars.kinzhunt.platform.apps.web.AppController.editApp(com.wstars.kinzhunt.platform.model.apps.Application,java.lang.Long)]
14:27:03.401 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'appController'
14:27:03.404 [main] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Reading [class com.wstars.kinzhunt.platform.model.apps.Application] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@5dc6bb75]
14:27:03.409 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
14:27:03.410 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13580800234
14:27:03.411 [main] DEBUG c.w.c.dao.hibernate.BaseDaoHibernate - Saving or Updating Object: com.wstars.kinzhunt.platform.model.apps.Application@1ba4f8a6[id=1,name=KinzHunt,company=com.wstars.kinzhunt.platform.model.apps.Company@6bc06877[id=1,name=KinzHunt],callbackUrl=http://www.kinzhunt.com/callback/,website=http://www.wstars.com/KinzHunt/,senderEmail=no-reply@kinzhunt.com,logoUrl=<null>]
14:27:03.412 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
14:27:03.412 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13580800234
14:27:03.413 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
14:27:03.422 [main] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
14:27:03.424 [main] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Written [1] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@5dc6bb75]
14:27:03.424 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
14:27:03.425 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request

如您所见,在第二个日志中,未创建预准备语句,也未打开任何JDBC连接。我对数据库的测试配置如下:

<bean id="targetDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:platform_test;DB_CLOSE_DELAY=-1" />
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="targetDataSource" />
    <property name="packagesToScan">
        <list>
            <value>com.mypackage.model.*</value>
        </list>
    </property>
    <property name="namingStrategy">
        <bean class="com.example.common.config.MyOwnNamingStrategy"/>
    </property>
    <property name="hibernateProperties">
        <map>
            <entry key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <entry key="hibernate.max_fetch_depth" value="1" />
            <entry key="hibernate.use_sql_comments" value="true" />
            <entry key="hibernate.hbm2ddl.auto" value="update" />
        </map>
    </property>
    <!-- <property key="hibernate.current_session_context_class" value="thread"/> -->
    <!-- <property key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JDBCTransactionFactory"/> -->
</bean>

<bean id="h2WebServer" class="org.h2.tools.Server"
    factory-method="createWebServer" depends-on="targetDataSource"
    init-method="start" lazy-init="false">
    <constructor-arg value="-web,-webPort,11111" />
</bean>

我的控制器代码是:

@Controller
public class AppController extends BaseAnnotatedController {

    @Autowired
    private AppManagementService appManagementService;

    @RequestMapping(value="/app/add", method=RequestMethod.POST, consumes={"application/json"})
    public @ResponseBody Long createApp(@RequestBody Application app) {
        saveApp(app);
        return app.getId();
    }

    @RequestMapping(value="/app/{appId}/edit", method=RequestMethod.POST, consumes={"application/json"})
    public @ResponseBody Long editApp(@RequestBody Application app, @PathVariable Long appId) {
        if (!appId.equals(app.getId())) {
            WSError error = new WSError(ValidationErrorType.GENERIC, "id");
            throw new ValidationException(error);
        } else {
            saveApp(app);
            return app.getId();
        }
    }

    @RequestMapping(value="/app/list", method=RequestMethod.GET)
    public @ResponseBody List<Application> listApps() {
        return appManagementService.listAllApps();
    }

    @RequestMapping(value="/app/{appId}/get", method=RequestMethod.GET)
    public @ResponseBody Application getAppDetails(@PathVariable Long appId) {
        return appManagementService.getApplication(appId);
    }

    private void saveApp(Application app) {
        if (isValid(app)) {
            appManagementService.saveApp(app);
        }
    }

    public @ResponseBody List<Application> listAllApps() {
        return appManagementService.listAllApps();
    }
}

我的测试课程是:

@Test
public class AppControllerIntegrationTests extends AbstractContextControllerTests {

    private MockMvc mockMvc;

    @Autowired
    AppController appController;

    @Autowired
    private AppManagementService appManagementService;

    @Autowired
    private BaseDao baseDao;

    @BeforeClass
    public void classSetup() {
        Company comp = new Company();
        comp.setName("Some Company");
        baseDao.saveObject(comp);
    }

    @BeforeMethod
    public void setup() throws Exception {
        this.mockMvc = webAppContextSetup(this.wac).build();
    }

    @Test
    public void testAddInvalidAppWebJson() throws Exception {
        String appJson = getInvalidApp().toJsonString();
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/app/add.json")
                .contentType(MediaType.APPLICATION_JSON).content(appJson);
        ResultActions resultAction = this.mockMvc.perform(requestBuilder);
        resultAction.andExpect(status().isForbidden());
        MvcResult mvcResult = resultAction.andReturn();
        Exception resolvedException = mvcResult.getResolvedException();
        assertTrue(resolvedException instanceof ValidationException);
        ValidationException validationException = (ValidationException) resolvedException;
        assertEquals(validationException.getErrors().size(), 3);
    }

    @Test
    public void testAddAppWebJson() throws Exception {
        Application app = getMockApp();
        String appJson = app.toJsonString();
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/app/add.json")
                .contentType(MediaType.APPLICATION_JSON).content(appJson);


        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());

    }

    @Test
    public void testEditAppWithWrongIdWebJson() throws Exception {
        String appJson = getMockAppWithId().toJsonString();
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/app/2/edit.json")
                .contentType(MediaType.APPLICATION_JSON).content(appJson);
        this.mockMvc
                .perform(requestBuilder)
                .andExpect(status().isForbidden())
                .andExpect(
                        content()
                                .string("{\"errors\":[{\"errorType\":\"-100\",\"field\":\"id\",\"constraint\":null}],\"objects\":null}"));
    }

    @Test(dependsOnMethods={"testAddApp", "testAddAppWebJson"})
    public void testEditAppWebJson() throws Exception {
        Application app = getMockAppWithId();
        setAppWebsiteToDifferentOne(app);
        String appJson = app.toJsonString();

        RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/app/1/edit.json")
                .contentType(MediaType.APPLICATION_JSON).content(appJson);

        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
    }

    @Test
    public void testEditInvalidAppWebJson() throws Exception {
        Application app = getMockAppWithId();
        app.setWebsite("Saba7o 3asal");
        String appJson = app.toJsonString();
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/app/1/edit.json")
                .contentType(MediaType.APPLICATION_JSON).content(appJson);
        this.mockMvc
                .perform(requestBuilder)
                .andExpect(status().isForbidden())
                .andExpect(
                        content()
                                .string("{\"errors\":[{\"errorType\":\"-114\",\"field\":\"website\",\"constraint\":null}],\"objects\":null}"));
    }

    @Test(dependsOnMethods="testEditAppWebJson")
    public void testGetAppDetails() throws Exception {
        RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/app/1/get.json");
        Application app = getMockAppWithId();
        setAppWebsiteToDifferentOne(app);

        this.mockMvc.perform(requestBuilder).andExpect(status().isOk())
                .andExpect(content().string(app.toJsonString()));
    }
}

我的服务方法是:

@Override
@Transactional(readOnly=false)
public void saveApp(Application app) {
    baseDao.saveObject(app);
}

除最后一个测试方法外,所有测试方法都通过,因为它期望应用程序的网站是编辑过的网站。我哪里出错了?

1 个答案:

答案 0 :(得分:1)

需要知道saveApp()中调用了哪种hibernate方法,同时确保使用@Transactional注释您的服务。