0 0

联合查询的问题,折腾了一天了10

一个网站项目,网站有类别,类别是树状的,但目前只有两层;类别下可以有相应的产品。
下面两个是相应的VO:
public class CategoryModel implements Serializable
{
	private int id;

	private String name;
	
	private int parentCategoryId;

	private CategoryModel parentCategory;

	//省略相应的setter/getter方法
	......
}

public class SoftwareModel implements Serializable
{
	private int id;

	private String name;

	private CategoryModel category;

	//省略相应的setter/getter方法
	......
}

下面是iBatis的XML文件:
<sqlmapconfig>
	<settings cachemodelsenabled="true" enhancementenabled="true" lazyloadingenabled="true" usestatementnamespaces="true">

  	<sqlmap resource="com/demo/category/Category.xml">
  	<sqlmap resource="com/demo/software/Software.xml">
</sqlmapconfig>

Category.xml文件
<sqlmap namespace="Category">

	<typealias alias="category" type="com.demo.category.model.CategoryModel">

	<cachemodel type="OSCACHE" id="category-cache">
		<flushinterval minutes="10">
		<flushonexecute statement="Category.addCartory">
	</cachemodel>

	<resultmap class="category" id="categoryResult">
		<result property="id" column="FID" javatype="int" jdbctype="int" nullvalue="0">
		<result property="name" column="FName" javatype="String" jdbctype="nvarchar">
		<result property="parentCategoryId" column="FParentID" javatype="int" jdbctype="int" nullvalue="0">
		<!---->
		<result property="parentCategory" column="FParentID" select="Category.getCategoryById">
	</resultmap>

	<!---->
	<select id="getCategoryById">
		SELECT * FROM t_categories 
		WHERE FID =#categoryId#
	</select>
</sqlmap>

Software.xml文件:
<sqlmap namespace="Software">

	<typealias alias="software" type="com.demo.software.model.SoftwareModel">

	<cachemodel type="OSCACHE" id="software-cache">
		<flushinterval minutes="10">
		<flushonexecute statement="Software.addSoftware">
	</cachemodel>

	<resultmap class="software" id="softwareResult">
		<result property="id" column="FID" javatype="int" jdbctype="int" nullvalue="0">
		<result property="name" column="FName" javatype="String" jdbctype="nvarchar">
		<result property="category" column="FCategoryID" select="Category.getCategoryById">
	</resultmap>

	<select id="getSoftwareById">
		SELECT * FROM t_softwares 
		WHERE FID =#softwareId#
	</select>
	
	<select id="getSoftwareByName">
		SELECT * FROM t_softwares 
		WHERE FName =#name#
	</select>
</sqlmap>

通过Spring来调用iBatis,下面是Spring的配置文件:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
		
	<bean id="messageProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:properties/cms.properties</value>
			</list>
		</property>
	</bean>	
	
	<bean id="cmsDbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName">
			<value>${cms.datasource.driverClassName}</value>
		</property>
		<property name="url">
			<value>${cms.datasource.url}</value>
		</property>
		<property name="username">
			<value>${cms.datasource.username}</value>
		</property>
		<property name="password">
			<value>${cms.datasource.password}</value>
		</property>
	</bean>

	<bean id="cmsSqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
		<property name="configLocation">
			<value>classpath:cms-sql-map-config.xml</value>
		</property>
		<property name="dataSource">
			<ref bean="cmsDbcpDataSource">
		</property>
	</bean>
	
	<bean id="cmsSqlMapClientTemplete" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
		<property name="sqlMapClient">
			<ref bean="cmsSqlMapClient">
		</property>
	</bean>

	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource">
			<ref local="cmsDbcpDataSource">
		</property>
	</bean>

	<bean id="baseTxProxy" lazy-init="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
		<property name="transactionManager">
			<ref local="transactionManager">
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="*">PROPAGATION_REQUIRED</prop>
				<prop key="create*">PROPAGATION_REQUIRED</prop>
				<prop key="add*">PROPAGATION_REQUIRED</prop>
				<prop key="delete*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="get*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>

	<bean id="categoryDao" class="com.demo.category.dao.impl.ibatis.CategoryDaoImpl">
		<property name="cmsSqlMapClientTemplete">
			<ref bean="cmsSqlMapClientTemplete">
		</property>
	</bean>
	
	<bean id="softwareDao" class="com.demo.software.dao.impl.ibatis.SoftwareDaoImpl">
		<property name="cmsSqlMapClientTemplete">
			<ref bean="cmsSqlMapClientTemplete">
		</property>
	</bean>
</beans>

下面是相应的DAO:
public abstract class BaseDaoImpl
{
	private SqlMapClientTemplate cmsSqlMapClientTemplete;

	public SqlMapClientTemplate getCmsSqlMapClientTemplete()
	{
		return cmsSqlMapClientTemplete;
	}

	public void setCmsSqlMapClientTemplete(
			SqlMapClientTemplate cmsSqlMapClientTemplete)
	{
		this.cmsSqlMapClientTemplete = cmsSqlMapClientTemplete;
	}
}

public class SoftwareDaoImpl extends BaseDaoImpl implements SoftwareDao
{

	@Override
	public SoftwareModel getSoftwareById(int softwareId)
	{
		return (SoftwareModel) getCmsSqlMapClientTemplete().queryForObject("Software.getSoftwareById", new Integer(softwareId));
	}

	@Override
	public SoftwareModel getSoftwareByName(String name)
	{
		return (SoftwareModel) getCmsSqlMapClientTemplete().queryForObject("Software.getSoftwareByName", name);
	}
}

public class CategoryDaoImpl extends BaseDaoImpl implements CategoryDao
{

	@Override
	public CategoryModel getCategoryById(int categoryId)
	{
		return (CategoryModel) getCmsSqlMapClientTemplete().queryForObject("Category.getCategoryById", new Integer(categoryId));
	}
	
	@Override
	public CategoryModel getCategoryByName(String name)
	{
		return (CategoryModel) getCmsSqlMapClientTemplete().queryForObject("Category.getCategoryByName", name);
	}
}

如果调用以下直接取得类别的代码不会有任何问题,证明这样子来取父类别是没有任何问题的
CategoryModel model = categoryDao.getCategoryById(273);

但是下面是取产品的代码就会抛出异常:
SoftwareModel model = softwareDao.getSoftwareById(111);//抛出异常
SoftwareModel model = softwareDao.getSoftwareByName("aaa");//抛出异常

当把Category.xml文件中“<result property="parentCategory" column="FParentID" select="Category.getCategoryById">”这一行注释掉后,上面代码就能正确运行。
下面是取产品代码的异常堆栈:
org.springframework.jdbc.UncategorizedSQLException: SqlMapClient operation; uncategorized SQLException for SQL []; SQL state [HY010]; error code [0];
--- The error occurred in com/demo/software/Software.xml.
--- The error occurred while applying a result map.
--- Check the Software.softwareResult.
--- Check the result mapping for the 'category' property.
--- Cause: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/category/Category.xml.
--- The error occurred while applying a result map.
--- Check the Category.categoryResult.
--- The error happened while setting a property on the result object.
--- Cause: java.sql.SQLException: Invalid state, the ResultSet object is closed.; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/software/Software.xml.
--- The error occurred while applying a result map.
--- Check the Software.softwareResult.
--- Check the result mapping for the 'category' property.
--- Cause: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/category/Category.xml.
--- The error occurred while applying a result map.
--- Check the Category.categoryResult.
--- The error happened while setting a property on the result object.
--- Cause: java.sql.SQLException: Invalid state, the ResultSet object is closed.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.translate(SQLStateSQLExceptionTranslator.java:124)
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.translate(SQLErrorCodeSQLExceptionTranslator.java:322)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:212)
at org.springframework.orm.ibatis.SqlMapClientTemplate.queryForObject(SqlMapClientTemplate.java:271)
at com.shareweb.cms.component.software.dao.impl.ibatis.SoftwareDaoImpl.getSoftwareById(SoftwareDaoImpl.java:23)
at com.shareweb.cms.importdata.AdvertExcelImportManagerImplTest.testImportData(AdvertExcelImportManagerImplTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/software/Software.xml.
--- The error occurred while applying a result map.
--- Check the Software.softwareResult.
--- Check the result mapping for the 'category' property.
--- Cause: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/category/Category.xml.
--- The error occurred while applying a result map.
--- Check the Category.categoryResult.
--- The error happened while setting a property on the result object.
--- Cause: java.sql.SQLException: Invalid state, the ResultSet object is closed.
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryWithCallback(MappedStatement.java:201)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryForObject(MappedStatement.java:120)
at com.ibatis.sqlmap.engine.mapping.statement.CachingStatement.executeQueryForObject(CachingStatement.java:79)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:518)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:493)
at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)
at org.springframework.orm.ibatis.SqlMapClientTemplate$1.doInSqlMapClient(SqlMapClientTemplate.java:273)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:209)
... 21 more
Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/demo/category/Category.xml.
--- The error occurred while applying a result map.
--- Check the Category.categoryResult.
--- The error happened while setting a property on the result object.
--- Cause: java.sql.SQLException: Invalid state, the ResultSet object is closed.
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryWithCallback(MappedStatement.java:201)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryForObject(MappedStatement.java:120)
at com.ibatis.sqlmap.engine.mapping.statement.CachingStatement.executeQueryForObject(CachingStatement.java:79)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:518)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:493)
at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)
at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.queryForObject(SqlMapClientImpl.java:82)
at com.ibatis.sqlmap.engine.mapping.result.loader.ResultLoader.getResult(ResultLoader.java:75)
at com.ibatis.sqlmap.engine.mapping.result.loader.LazyResultLoader.loadResult(LazyResultLoader.java:77)
at com.ibatis.sqlmap.engine.mapping.result.loader.ResultLoader.loadResult(ResultLoader.java:54)
at com.ibatis.sqlmap.engine.mapping.result.ResultMap.getNestedSelectMappingValue(ResultMap.java:501)
at com.ibatis.sqlmap.engine.mapping.result.ResultMap.getResults(ResultMap.java:341)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.java:384)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleMultipleResults(SqlExecutor.java:300)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:189)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteQuery(MappedStatement.java:221)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryWithCallback(MappedStatement.java:189)
... 28 more
Caused by: java.sql.SQLException: Invalid state, the ResultSet object is closed.
at net.sourceforge.jtds.jdbc.JtdsResultSet.checkOpen(JtdsResultSet.java:299)
at net.sourceforge.jtds.jdbc.JtdsResultSet.next(JtdsResultSet.java:569)
at org.apache.commons.dbcp.DelegatingResultSet.next(DelegatingResultSet.java:169)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.ibatis.common.jdbc.logging.ResultSetLogProxy.invoke(ResultSetLogProxy.java:47)
at $Proxy10.next(Unknown Source)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.java:383)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleMultipleResults(SqlExecutor.java:300)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:189)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteQuery(MappedStatement.java:221)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryWithCallback(MappedStatement.java:189)
... 44 more

问题补充
去iBatis官方论坛上也没有找到解决方法。难道没有人遇到过这种问题吗?
问题补充
首选,这个是lazyLoading的,所以如果没有调用getParentCategory()方法的话是不会真正去加载parent category的。
其次,当递归到根结点时就停了。
问题不在这个上面,如果是因为递归原因,为什么我直接CategoryModel model = categoryDao.getCategoryById(273); 就可以,而SoftwareModel model = softwareDao.getSoftwareById(111);却出错。
问题补充
在www.nabble.com上问了,也没有什么实质性的回答。大部份人认为可能是DB设置的问题,比如链接数是不是设置成1了。但我试过了,不是这个问题。javaeye的大侠们,没有人遇到过这种问题吗?
问题补充
如果用inner join的话,就不能通过po一级一级地得到父类别了。此外,即使通过别的方法解决了,但还是想从技术弄明白为什么不能这样做。
在www.nabble.com上有人说是配置问题,于是试了下面代码,代码运行没问题,证明DB配置是没有问题的。
Connection cn1 = null;
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
ResultSet rs1 = null;
ResultSet rs2 = null;
DataSource ds = (BasicDataSource)ctx.getBean("cmsDbcpDataSource");

try
{
// Class.forName("net.sourceforge.jtds.jdbc.Driver").newInstance();
// cn1 = DriverManager.getConnection("jdbc:jtds:sqlserver://localhost:1433/aa", "sa", "aa");
cn1 = ds.getConnection();
pst1 = cn1.prepareStatement("select fid, fname, fcategoryid from t_softwares");
rs1 = pst1.executeQuery();
while(rs1.next())
{
System.out.println("software:" + rs1.getString(2));
pst2 = cn1.prepareStatement("select fid, fname from t_categories where fid=?");
pst2.setInt(1, rs1.getInt(3));
rs2 = pst2.executeQuery();
if(rs2.next())
{
System.out.println("category:" + rs2.getString(2));
}
rs2.close();
pst2.close();
}
rs1.close();
pst1.close();
cn1.close();

}
catch(Exception e)
{
e.printStackTrace();
assertTrue(false);
}
问题补充
两周过去了,还没解决问题
2008年8月24日 19:52

4个答案 按时间排序 按投票排序

0 0

我只用ibatis试了下,没有问题。
可能问题出在与spring整合上。目前spring不熟,看来帮不上什么忙了。

2008年8月26日 17:33
0 0

不就是一个一对多的查询吗,ibatis的文档上就有啊,不要用<result property="category" column="FCategoryID" select="Category.getCategoryById" />  这样存在N+1次查询,ibatis帮助文档上有解决方案。我就不在啰嗦了,希望对你有点帮助

2008年8月24日 19:52
0 0

直接用inner join 实现联合查询就可以了不是么?

2008年8月24日 19:52
0 0

lz, 每得到出一个category都会查它的parent category,那这个递归什么时候停呢?

2008年8月24日 19:52

相关推荐

    Mysql多表联合查询

    MySql,多表联合查询加AS语句,多余的就不说了,自己看,绝对简单!

    access联合查询示例

    access 联合查询 示例 将两个以上数据表中的相关数据通过联合查询筛选出来,显示在一个查询数据表中。

    上传一个带合计的联合查询(转别人的,与大家分享)

    Access提供了多种查询方式,其中之一就是联合查询,这在处理多个表的数据整合时非常有用。"上传一个带合计的联合查询"这个标题揭示了我们要讨论的主题——如何在Access中进行复杂的联合查询,并且包含计算字段。 ...

    多表联合查询

    多表联合查询,一个简单的实例。适合初学者参考!

    SQL多表联合查询.rar

    总的来说,这个压缩包提供了一个实践性的学习资源,帮助用户理解并掌握SQL中的多表联合查询,同时通过VB项目示例展示了如何在实际开发中应用这些查询。用户不仅可以学习到SQL语法,还能了解到数据库应用程序的设计和...

    dataset 多表联合查询

    dataset 多表联合查询 dataset 多表联合查询 dataset 多表联合查询 dataset 多表联合查询 dataset 多表联合查询 dataset 多表联合查询 dataset 多表联合查询

    delphi access表的多表联合查询

    delphi针对 access表的多表联合查询实例

    SQL多条件联合查询.txt

    SQL同一个表不同条件两个SUM两个count查询结果一次查询并显示

    sql 联合查询 子查询 综合查询 统计数据

    sql 联合查询 子查询 综合查询 统计数据 case when then 用法

    sql注入之联合查询,最详细的CMS联合查询过程!!!!

    【联合查询】是SQL注入的一种技术,允许攻击者合并两个或更多的SELECT语句,以便在不直接访问数据库的情况下获取信息。在这个场景中,攻击者首先判断了注入点,通过对比两个不同的URL请求,发现系统对不同输入的响应...

    access仓库联合查询

    使用库管实例,简单实用,高手细化一下,互相交流一下。

    实例讲解MYSQL联合查询

    MySQL是一种广泛使用的开源关系型数据库管理系统,其联合查询(JOIN)功能是数据库操作中的核心部分,用于合并多个数据表中的相关记录,以便进行更复杂的分析和检索。在本实例讲解中,我们将深入探讨MySQL联合查询的...

    计算机病毒与防护:MYSQL联合查询上.ppt

    MySQL数据库中的联合查询(UNION)是一种强大的操作,它允许将两个或更多SELECT语句的结果合并成一个结果集。在理解联合查询时,我们需要明确几个关键概念: 1. **UNION操作符**:UNION操作符用于合并两个或多个...

    MySQL 联合查询案例代码

    MySQL 联合查询案例,需要搭配博主【MySQL】联合查询这篇文章使用,用于初始化数据,搭配文章学习可以充分学会联合查询,建议先学习博主 MySQL 专栏前部分的基础知识,联合查询是在有一定基础后进行学习的,里面的...

    联邦学习与数据库联合查询.pptx

    - **联合查询定义**:数据库联合查询是一种能够在多个数据源或数据库之间执行查询的技术。通过整合来自不同来源的数据,联合查询可以帮助用户获得更全面的信息视图。 - **挑战**: - **数据异构性**:不同数据库的...

    很强大的jsp手写分页插件 实现json 等一些ajax技术 联合查询 模糊查询 条件查询

    5. **联合查询**:联合查询是数据库操作中的一种,将两个或多个表的数据合并在一起,以满足特定的查询需求。在分页插件中,联合查询可能用于整合不同表中的信息,提供更全面的数据展示。 6. **模糊查询**:模糊查询...

    小议ORACLE和MYSQL的联合查询差异.pdf

    然而,在实际项目中,当从Oracle切换到MySQL时,可能会遇到联合查询性能下降甚至引发内存溢出的问题。这主要是因为Oracle和MySQL在处理查询优化、内存管理和并行执行策略上的差异。 Oracle在处理联合查询时,通常会...

    处理group by 查询速度太慢的问题 数据量大.doc

    在实际项目中,遇到了表数据量大导致查询速度很慢的问题。通过记录和优化过程,总结出一些有价值的经验,希望能够帮助读者解决类似的问题。 知识点1:Group By 查询的索引设置 在优化 Group By 查询时,仅对 Group...

    73 通过一步一图来深入理解联合索引查询原理以及全值匹配规则l.pdf

    联合索引查询原理: 联合索引是由多个字段组成的索引结构,它的数据页内部按照联合索引的字段顺序进行排序。以本案例中的学生信息表为例,表中包含学生班级、学生姓名、科目名称和成绩分数四个字段,查询频率较高的...

Global site tag (gtag.js) - Google Analytics