1

我面临一个奇怪的问题。我目前正在编写一个基于 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

可以看到,在第二个日志中,没有创建prepared statement,也没有打开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);
}

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

4

1 回答 1

2

需要知道在 saveApp() 中调用了哪个休眠方法,还需要确保您的服务使用 @Transactional 进行注释。

于 2013-01-13T13:48:45.340 回答