`
xnxqs
  • 浏览: 11947 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

ibatis2.3.4+spring2.5通用DAO层的注解测试 + 1:N的n+1问题的解决思路(没实现)!附源码!

阅读更多
<![CDATA[ 到新单位一个星期了。研究以前没用过的ibats也有一个星期了,期间为了一个问题的实现用了N次GOOLE,最终还是自己解决了。发此帖的目的:
1、为了共享解决方案(在SQL2000下如何取得自增长ID,就本人亲测网上好多方法要么有隐患,要么无法实现。)
2、讨论若干疑问。
废话少说,直接上代码了。
[code="java"]
package com.ibatis.sample.bean;

import java.io.Serializable;

import org.springframework.stereotype.Repository;

/**
 * @author summerPine(沙漠派)
 * <Pre>
 * email:xnxqs@163.com
 * QQ:254120499
 * MSN:xnxqs@hotmail.com
 * 2009-7-20 下午02:12:05
 * ibatisTest
 * 转载请保留!  ^_^
 *</pre> 
 *creat sql:
 *if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t_user]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[t_user]
GO

CREATE TABLE [dbo].[t_user] (
	[id] [int] IDENTITY (1, 1) NOT NULL ,
	[name] [varchar] (10) COLLATE Chinese_PRC_CI_AS NULL ,
	[sex] [int] NULL 
) ON [PRIMARY]
GO
 */
@Repository
public class User implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String name;
	private Integer sex;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getSex() {
		return sex;
	}
	public void setSex(Integer sex) {
		this.sex = sex;
	}

	// public Set<Address> getAddresses() {
	// return addresses;
	// }
	//
	// public void setAddresses(Set<Address> addresses) {
	// this.addresses = addresses;
	// }

	// private Set<Address> addresses = new HashSet<Address>();

}

[/code]
简单的javabean,这个就不解说了吧。接下来是xml
[code="xml"]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="User">
	<typeAlias alias="user" type="com.ibatis.sample.bean.User" />
	<select id="getUser" parameterClass="user" resultClass="user">
		select * from t_user where 1=1
		<isNotEmpty prepend="AND" property="name">
			name=#name#
		</isNotEmpty>
		<isNotEmpty prepend="AND" property="sex">
			sex=#sex#
		</isNotEmpty>
		<isNotEmpty prepend="AND" property="id">
			id=#id#
		</isNotEmpty>
	</select>

	<update id="updateUser" parameterClass="user">
		UPDATE t_user
		SET
		name=#name#,
		sex=#sex#
		WHERE id = #id#
	</update>
	<update id="updateUserByName" parameterClass="java.lang.String">
		UPDATE t_user
		SET
		sex=#sex#
		WHERE name = #name#
	</update>
<!--[color=red]注一[/color]:就是这样地方花了我整整二天时间,网上的答案主要用二种,一种ibatis指南上推荐使用的:@@IDENTITY还有一种就如下面所示: SCOPE_IDENTITY(),按sql的帮助文档上所说:
SCOPE_IDENTITY 和 @@IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回插入到当前作用域中的值;@@IDENTITY 不受限于特定的作用域。

所以对于@@IDENTITY这个函数的使用,实质上是有一定隐患的,而一些网友筒志也有这种认识所以就有了下面的配置②,但经测试(测试环境:sql2000+sql2005的驱动包+myeclipse7.5)这样是无法取得返回ID的。因为ibatis把它当二条语句执行了,不在同一域所以没法取得ID值。(但本人所搜索出来的资料大都如此配置,所以对这个测试结果本人很不自信,不知道有没有朋友遇到过同样的问题。)无赖之下,捣鼓了半年,搞出了下面这种配置。经测试是绝对有效的。
-->
	<insert id="addUser" parameterClass="user">
		select 1;
		<selectKey resultClass="int" keyProperty="id" type="post">
			INSERT
			INTO t_user (name,sex)VALUES (#name#,#sex#)
			SELECT SCOPE_IDENTITY() AS
			ID
		</selectKey>

	</insert>
<!--配置2insert id="addUser" parameterClass="user">
		INSERT	INTO t_user (name,sex)VALUES (#name#,#sex#)
		<selectKey resultClass="int" keyProperty="id" type="post">			
			SELECT SCOPE_IDENTITY() AS ID
		</selectKey>

	</insert-->
	<delete id="deleteUser" parameterClass="user">
		delete from t_user
		<dynamic prepend="WHERE">
			<isNotEmpty prepend="AND" property="id">
				id = #id#				
			</isNotEmpty>
			<isNotEmpty prepend="AND" property="name">
				name = #name#
			</isNotEmpty>
			<isNotEmpty prepend="AND" property="sex">
				sex = #sex#
            </isNotEmpty>
		</dynamic>
	</delete>
</sqlMap>
[/code]
接下来是Dao层。
[code="java"]
package com.ibatis.sample.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ibatis.sqlmap.client.SqlMapClient;

public class BaseDao extends SqlMapClientDaoSupport { 
	@Autowired//为了注入SqlMapClient所以多了一个baseDao
	public void setSqlMapClientBase(SqlMapClient sqlMapClient) {
		super.setSqlMapClient(sqlMapClient);
	}
	
	
}
[/code]
接口
[code="java"]
package com.ibatis.sample.dao;

import java.util.List;

import com.ibatis.sample.bean.User;

public interface IUserDao {

	public User addUser(User user);

	public int delUser(User user);

	public void delUserById(User user);

	public void delUserByName(User user);

	public int updateUser(User user);

	public User getUser(User user);

	public  List<User> getUsers(User user);
}
[/code]
实现:
[code="java"]
package com.ibatis.sample.dao.impl;

import java.sql.SQLException;
import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ibatis.sample.bean.User;
import com.ibatis.sample.dao.BaseDao;
import com.ibatis.sample.dao.IUserDao;

@Service
@Transactional
public class UserDao extends BaseDao implements IUserDao {

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#insertUser(com.ibatis.sample.bean
	 * .User)
	 */
	public User addUser(User user) {
		Integer id = (Integer) getSqlMapClientTemplate()
				.insert("addUser", user);
		// this.getSqlMapClient()
		if (id != null)
			user.setId(id);
		else
			user = null;
		return user;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#delUser(com.ibatis.sample.bean.User)
	 */
	public int delUser(User user) {
		return getSqlMapClientTemplate().delete("deleteUser", user);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#delUserById(com.ibatis.sample.bean
	 * .User)
	 */
	public void delUserById(User user) {
		getSqlMapClientTemplate().delete("updateUserById", user);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#delUserByName(com.ibatis.sample.bean
	 * .User)
	 */
	public void delUserByName(User user) {
		getSqlMapClientTemplate().delete("updateUserByName", user);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#updateUser(com.ibatis.sample.bean
	 * .User)
	 */
	public int updateUser(User user) {
		return getSqlMapClientTemplate().update("updateUser", user);
	}

	public void updateUserByName(User user) {
		getSqlMapClientTemplate().update("updateUserByName", user);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#getUser(com.ibatis.sample.bean.User)
	 */
	@Transactional(readOnly = true)
	public User getUser(User user) {
		return (User) getSqlMapClientTemplate().queryForObject("getUser", user);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.ibatis.sample.dao.impl.IUserDao#getUsers(com.ibatis.sample.bean.User)
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<User> getUsers(User user) {
		return (List) getSqlMapClientTemplate().queryForList("getUser", user);
	}
}
[/code]
测试类:
[code="java"]
package com.ibatis.sample.test;

import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

import com.ibatis.sample.bean.User;
import com.ibatis.sample.dao.IUserDao;

/**
 * @author summerPine(沙漠派)
 * 
 * <Pre>
 * email:xnxqs@163.com
 * QQ:254120499
 * MSN:xnxqs@hotmail.com
 * 2009-7-15 下午03:32:39
 * ibatisTest
 * 转载请保留!  ˆ_ˆ
 *</pre>
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserDaoTest {
	@Autowired
	IUserDao userDao;
	@Autowired
	User user;

	static List<User> userList = new ArrayList<User>();

	@Before
	public void setUp() throws Exception {
		System.out.println("setup被调用 ");
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
		for (User u : userList) {
			System.out.println(u.getId() + "--" + u.getName() + "--"
					+ u.getSex());
		}

	}

	@After
	public void tearDown被调用() throws Exception {
		if (user != null)
			System.out.println("单个对象为:" + user.getId() + "--" + user.getName()
					+ "--" + user.getSex());

		System.out.println("tearDown被调用 ");
	}

	 @Test
		public void testGetUser() {
			
			user.setId(60);
			user = userDao.getUser(user);
//			Assert.assertEquals(1, user.getId().intValue());
		}
	
	// @Test
	// @Rollback(false)
	// @NotTransactional
	public void insertUser() {
		user.setName("a2");
		user.setSex(2);
		user = userDao.addUser(user);

	}

	// @Test
	public void testDelUser() {
		user = new User();
		user.setId(17);// 请填入一个存在的ID,否则不通过
		System.out.println("**************************");
		int rows = userDao.delUser(user);
		Assert.assertFalse(rows == 0);
	}

//	@Test
	public void testUpdateUser() {

		user.setId(4);
		user = userDao.getUser(user);
		System.out.println("改前为:" + user.getId() + "--" + user.getName() + "--"
				+ user.getSex());
		user.setName("改动ING");
		user.setSex(5);
		int rows = userDao.updateUser(user);

		Assert.assertFalse(rows == 0);
		System.out.println("改后为:" + user.getId() + "--" + user.getName() + "--"
				+ user.getSex());
	}

	//
	
	//
//	  @Test
	 public void testGetUsers() {
		  user = new User();
		  user.setName("a2");
		  userList = userDao.getUsers(user);
	 }

}
[/code]
写到这里的时候,以上测试全部通过,但实际上这个例子太简单了,1.没有service,直接上的dao层,分层概念没有体现出来。2、没有测试事务。如是本人又有了以下的扩展测试:
扩展一:通用DAO。
扩展二:加了service层
扩展三:1:N的数据关联查询
扩展四:尝试1:n,N:M的N+1问题,没成功(最开始是因为对ibatis了解得太少,到后期了解稍深入一点以后,就发现自己的想法太天真了,但个人觉得还是有那么一点点一点的可取之处,所以在这里只会帖出扩展四的部分代码,以抛砖引玉。PS:我已运气于顶,请大家拍吧。。)。
javaBean
[code="java"]
package com.ibatis.sample.bean;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Repository;

@Repository
public class UserAndAddress implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String name;
	private Integer sex;
	/**
	 * 配置①begin...
	 * 常用的(1:N)配置(无法解决N+1问题),测试时请根据具体的测试方法注释以下部分
	 * (测试UserAndAddressServiceTest内的testTr2方法时请注释以下部分)
	 * 注:本人在这里没有用List的返回类型。(个人意见:) 用set是因为对持久层的东西来说,最好让pojo唯一,以防脏数据,
	 * 在hibernate中用得最多的就是set了。但这样做很麻烦,不知道是否可取,欢迎拍砖。
	 * 
	 * */
	 private Set<Address> addresses;
	 public Set<Address> getAddresses() {
	 return addresses;
	 }
	 public void setAddresses(List<Address> addresses) {
	 this.addresses =new HashSet<Address>(addresses);
	 }
	 /**配置①end**/
	 
	 
	/***
//	 * 配置②begin...
//	 * 避免N+1(1:N、M:N)问题的例子begin******
//	 * (测试UserAndAddressServiceTest内的testTr1方法时请注释以下部分)
////	 * ****/
//	private List<Address> addresses = new MyArrayList<Address>(0);
//
//	public List<Address> getAddresses() {
//		return addresses;
//	}
//
//	public void setAddresses(List<Address> addresses) {
//		this.addresses.addAll(addresses);
//	}
	 /**配置②end**/

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getSex() {
		return sex;
	}

	public void setSex(Integer sex) {
		this.sex = sex;
	}

}

[/code]
xml
[code="xml"]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="UserAndAddress">
	<typeAlias alias="userAndAddress" type="com.ibatis.sample.bean.UserAndAddress" />
	<typeAlias alias="address" type="com.ibatis.sample.bean.Address" />
	<resultMap id="get-user-result" class="userAndAddress">
		<result property="id" column="id" />
		<result property="name" column="name" />
		<result property="sex" column="sex" />
		<!--<result property="addresses" column="id" select="userAndAddress.getAddressByUserId" />如果启用命名空间-->
		<result property="addresses" column="id" select="getAddressByUserId" />
	</resultMap>	
	

	<select id="getUserAndAddress" parameterClass="userAndAddress" resultClass="userAndAddress">
		select * from t_user where 1=1
		<isNotEmpty prepend="AND" property="name">
			name=#name#
		</isNotEmpty>
		<isNotEmpty prepend="AND" property="sex">
			sex=#sex#
		</isNotEmpty>
		<isNotEmpty prepend="AND" property="id">
			id=#id#
		</isNotEmpty>
	</select>

	<select id="searchUserAndAddress" parameterClass="userAndAddress"
		resultMap="get-user-result">
		select * from t_user
		<dynamic prepend="WHERE">
			<isNotEmpty prepend="AND" property="name">
				name=#name#
		</isNotEmpty>
			<isNotEmpty prepend="AND" property="sex">
				sex=#sex#
		</isNotEmpty>
			<isNotEmpty prepend="AND" property="id">
				id=#id#
		</isNotEmpty>
		</dynamic>
	</select>
	
	<select id="getAddressByUserId" parameterClass="Integer"
		resultClass="address">

select
address,
zipcode
from t_address
where user_id = #userid#
]]>
	
<!--重复部分省了。。。。。-->

[/code]



javaBean

[code="java"]
package com.ibatis.sample.bean;

import org.springframework.stereotype.Repository;

/**
 * @author summerPine(沙漠派)
 * 
 * email:xnxqs@163.com
 * QQ:254120499
 * MSN:xnxqs@hotmail.com
 * 2009-7-20 下午02:13:18
 * ibatisTest
 * 转载请保留!  ^_^
 *
*creat sql *if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t_address]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[t_address] GO CREATE TABLE [dbo].[t_address] ( [id] [int] IDENTITY (1, 1) NOT NULL , [address] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL , [zipcode] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL , [user_id] [int] NULL ) ON [PRIMARY] GO */ @Repository public class Address { String address; String zipcode; int id; //无法实现双向,下面这个属性其实没用上。按ibatis的refence推荐:写二个address。一个带UserAndAddress ,一个不带。 UserAndAddress userAndAddress; public UserAndAddress getUserAndAddress() { return userAndAddress; } public void setUserAndAddress(UserAndAddress userAndAddress) { this.userAndAddress = userAndAddress; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public int getId() { return id; } public void setId(int id) { this.id = id; } } [/code] 通用DAO接口: [code="java"] package com.ibatis.sample.dao; import java.io.Serializable; import java.util.List; public interface IModelDao { public ID create(T value); public int update(T value); public int delete(T value); public List getObjects(T value); public T getObjectById(T value); public List getAnyObjects(String str,Object entity); } [/code] 通用DAO实现 [code="java"] package com.ibatis.sample.dao; import java.io.Serializable; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ibatis.sample.comm.Util; /** * @author summerPine(沙漠派) * *
 * email:xnxqs@163.com
 * QQ:254120499
 * MSN:xnxqs@hotmail.com
 * 2009-7-15 下午04:00:31
 * ibatisTest
 * 转载请保留!  ˆ_ˆ;
 *
* [color=red]注二[/color]: * 模式DAO,实际应用中只用这一个DAO即可。 继承BaseDao是为了注入sqlmapclient * 至于为什么要实现IModelDao这样的一个接口基于以下原因: 1.为了简化配置,只用一种代理(JDK的动态代理机制), * (如果系统对性能要求比较高,并发性较高的话,建议采用CGLIB方式产生代理实例, * 具体这方面的配置请参阅spring文档,并加上CGLIB包。) * * 注:因为本Dao只是一个例子,所以只写了最基本的CRUD,其实本可以扩展得更多的, * 但太麻烦了,本人将在后期奉上。至于有的朋友可能会问到有没有HIBERNATE的通用包, * 在这里帮白衣兄打打广告以视支持:强烈推荐去观看springSide的DAO部分的源码 * */ // 另,关于spring的事务控制问题,spring文档上说: // // “请注意Spring框架的事务基础架构代码将默认地 只 在抛出运行时和unchecked exceptions时才标识事务回滚。 也就是说,当抛出一个 // RuntimeException 或其子类例的实例时。(Errors 也一样 - 默认地 - 标识事务回滚。)从事务方法中抛出的Checked // exceptions将 不 被标识进行事务回滚。” // 在这里,我一起有一个疑问就是,要不要在DAO层抛出异常??抛出什么样的异常为好?(针对spring2.5) // 目前本人的做法就是DAO层不管,在service层捕捉Uncheck异常,抛出一个自定义的runtimeException,然后在UI层捕捉错误。理由: 1、这是个通用DAO,有异常也属于不可复的底层异常。把这种底层异常交给service来处理,可以根据实际情况把这种异常包装一下,并抛给UI层一个对用户来说有意义的异常。(比如说删除时,如果ID不存在,其实是不会报错的,但我可以包装一下,然后交给ui层,提示ID不存在,再比如说针对一些不可恢复的异常,我们也可以包装一下,以便让程序继续走下去。) 2、理由二,看了javaeye上一位大大写的《关于j2ee的异常处理。。》报歉,本人记不清全称了和作者了,上面也是这样推荐的。 不知道可不可取?请来者拍砖并给出理由。 // 这里加上service是为了给懒人准备的,有了它就可以少写一个跟实体类相关的dao层了。可以直接在service层用了。 @Service public class IbatisDao extends BaseDao implements IModelDao { private static Log logger = LogFactory.getLog(IbatisDao.class); //如果sql map的xml中存在重复的方法,可以考虑启用命名空间 eg. namespce.getUser(); // private String BeanNameSpace = getPersistentClass().getName()+"."; // 常用操作的命名规范为: SQL_操作命+实例类型名称, // 比如说实例 u是User类型,现在要在数据库内添加 一行记录那么SQL_Add+Util.getTName(entity) // 返回原就是addUser字符串 private final static String SQL_Add = "add"; private final static String SQL_DELETE = "delete"; private final static String SQL_UPDATE = "update"; private final static String SQL_Get = "get"; private final static String SQL_SEARCH = "search"; // 保持实体对象类的类型 private Class persistentClass; public void setPersistentClass(Class persistentClass) { this.persistentClass = persistentClass; } protected Class getPersistentClass() { return persistentClass; } // 觉得这个带参的构造函数还是很有必要的,多一个有备无患嘛,虽然在我的应用里面看不出它的作用。 @SuppressWarnings("unchecked") public IbatisDao(Class clazz) { this.persistentClass = clazz; } // 有了上面带参的construct,这里必须再写一个无参的construct否则代理类会找不到构造函数,无 法注入了。 public IbatisDao() { super(); } /* * (non-Javadoc) * * @see com.ibatis.sample.dao.IModelDao#create(java.lang.Object) */ @SuppressWarnings("unchecked") public ID create(T entity) { logger.info("正在添加对象" + entity.getClass().getName()); ID id = (ID) getSqlMapClientTemplate().insert( SQL_Add + Util.getTName(entity), entity); if (id != null) logger.info("添加成功!");// 在这里不做判断直接写,会不会有什么问题? return id; } public int delete(T entity) { logger.info("正在删除对象" + entity.getClass().getName()); int rows = getSqlMapClientTemplate().delete( SQL_DELETE + Util.getTName(entity), entity); if (rows != 0) logger.info("删除成功!"); return rows; } @SuppressWarnings("unchecked") @Transactional(readOnly = true) // 查询语句一律不通过事务,以减少资源 损耗,加快速度。 public T getObjectById(T entity) { logger.info("正在获取对象" + entity.getClass().getName()); entity = (T) getSqlMapClientTemplate().queryForObject( SQL_Get + Util.getTName(entity), entity); if (entity != null) logger.info("获取成功!"); return entity; } @SuppressWarnings("unchecked") @Transactional(readOnly = true) // 采取这种方法搜索对象时,如果想获得表内所有记录,应该传与一个空的实例,比如说 new User() public List getObjects(T entity) { logger.info("正在获取对象列表" + entity.getClass().getName()); List list = getSqlMapClientTemplate().queryForList( SQL_SEARCH + Util.getTName(entity), entity); if (list != null && list.size() > 0) logger.info("获取对象列表成功!"); return list; } @SuppressWarnings("unchecked") // @Transactional(readOnly = true)// // 如果要用上本方法必须先设置persistentClass,eg.调用setpersistentClass(); //但这种写法不是很好。会让人莫名 其妙为什么调用 setpersistentClass。建议在实际开发中多写一条可以查询所有对象的maping语句。 public List getAllObjects() { T t=null; try { t = persistentClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (t == null) return null; else return getObjects(t); } @SuppressWarnings("unchecked") @Transactional(readOnly = true) // 采取这种方法搜索对象时,如果想获得表内所有记录,应该传与一个空的实例,比如说 new User() public List getAnyObjects(String str,Object entity) { logger.info("正在获取对象列表" + entity.getClass().getName()); List list = getSqlMapClientTemplate().queryForList(str, entity); if (list != null && list.size() > 0) logger.info("获取对象列表成功!"); return list; } public int update(T entity) { logger.info("正在更新对象" + entity.getClass().getName()); int rows = getSqlMapClientTemplate().update( SQL_UPDATE + Util.getTName(entity), entity); if (rows > 0) logger.info("更新成功!"); return rows; } } [/code] 基础service的接口 [code="java"] package com.ibatis.sample.service; import java.io.Serializable; import com.ibatis.sample.dao.IModelDao; //service层的基类,主要是为了得到dao,有了它,就算把DAO层改得一埸湖涂也方便以后扩展, //我现在的单位就因为以前的东西写得太死,所以好多项目的维护已经惨不忍睹了。这里再次深刻体会到分层的好处。 public interface BaseService{ public void setIbatisDao(IModelDao modelDao); public IModelDao getIbatisDao(); } [/code] [color=red]注:[/color] 具体业务(service)层的接口 我在这里把service层直接当业务层,不知行不行?有时个人觉得service上面可以再发一个biz层出来专门跟业务打交道,不知道那种更可取,请拍砖。。 [code="java"] package com.ibatis.sample.service; import com.ibatis.sample.bean.UserAndAddress; public interface IUserAndAddressService extends BaseService{ public void printUserAndAddress(); public void printUserAndAddressQ(); } [/code] 实现类 [code="java"] package com.ibatis.sample.service.impl; import java.util.List; import javax.annotation.Resource; import javax.el.MethodInfo; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ibatis.sample.bean.Address; import com.ibatis.sample.bean.UserAndAddress; import com.ibatis.sample.bean.UserAndAddress1; import com.ibatis.sample.comm.excep.ServiceException; import com.ibatis.sample.dao.IModelDao; import com.ibatis.sample.service.IUserAndAddressService; /** * @author summerPine(沙漠派) *
 * email:xnxqs@163.com
 * QQ:254120499
 * MSN:xnxqs@hotmail.com
 * 2009-7-17 下午03:30:58
 * ibatisTest
 * 转载请保留!  ^_^
 *
*一对多的测试,为了跟让大家看得更方便点,不影响userService的测试。就跟UserService分开写了。 */ @Service public class UserAndAddressService implements IUserAndAddressService { private IModelDao ibatisDao; public IModelDao getIbatisDao() { return ibatisDao; } @Resource public void setIbatisDao(IModelDao modelDao) { this.ibatisDao = modelDao; } // @Transactional public void printUserAndAddress() throws ServiceException{ try { List list=ibatisDao.getObjects(new UserAndAddress()); if(list !=null) for(UserAndAddress ua:list){ System.out.println("用户:"+ua.getName()+"的住址是:"); for(Address ad:ua.getAddresses()){ System.out.println(" "+ad.getAddress()); } } } catch (Exception e) { throw new ServiceException("查询失败",e); } } //换个方法取得用户名和地址,如果成功速度较快。 public void printUserAndAddressQ() throws ServiceException{ try { List list=ibatisDao.getAnyObjects("getUserAddress",new UserAndAddress1()); if(list !=null) for(UserAndAddress ua:list){ System.out.println("用户:"+ua.getName()+"的住址是:"); for(Address ad:ua.getAddresses()){ System.out.println(" "+ad.getAddress()); } } } catch (Exception e) { throw new ServiceException("查询失败",e); } } } [/code] 测试方法: [code="java"] package com.ibatis.sample.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.ibatis.sample.service.IUserAndAddressService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class UserAndAddressServiceTest { @Resource //spring推荐用注解,与@Autowired相比可更少 的依赖spring,不过这个注解是根据name来注入的。 IUserAndAddressService userAndAddressService; @Test public void testTr() { try { userAndAddressService.printUserAndAddress(); } catch (Exception e) { e.printStackTrace(); } } // @Test 测试1:n的N+1问题,暂时没法实现。 public void testTr2() { try { userAndAddressService.printUserAndAddressQ(); } catch (Exception e) { e.printStackTrace(); } } } [/code] sql-map-config.xml [code="xml"] <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"> <!-- 当userStatementNamespaces为false时,要注意映射文件中,Statement定义无重名。 --> <!-- typeAlias alias="mapBean" type="com.vsc.po.MapBean" / --> <!--<transactionManager type="JDBC"> <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="com.microsoft.jdbc.sqlserver.SQLServerDriver" /> <property name="JDBC.ConnectionURL" value="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=rbac;SelectMethod=Cursor" /> <property name="JDBC.Username" value="sa" /> <property name="JDBC.Password" value="sa" /> <property name="Pool.MaximumActiveConnections" value="10" /> <property name="Pool.MaximumIdleConnections" value="5" /> <property name="Pool.MaximumCheckoutTime" value="120000" /> <property name="Pool.TimeToWait" value="500" /> <property name="Pool.PingQuery" value="select 1 T_user" /> <property name="Pool.PingEnabled" value="false" /> <property name="Pool.PingConnectionsOlderThan" value="1" /> <property name="Pool.PingConnectionsNotUsedFor" value="1" /> </dataSource> </transactionManager>--> <!-- sqlMap resource="com/ibatis/sample/bean/UserAndAddress1.xml"/--> [/code] Srpring.xml [code="xml"] <?xml version="1.0" encoding="UTF-8"?> classpath:DB.properties <!--<bean id="userDao" class="com.ibatis.sample.dao.impl.UserDao"> <property name="dataSource" ref="dataSource" /> <property name="sqlMapClient" ref="sqlMapClient" /> </bean> --> <!-- a PlatformTransactionManager is still required --> <!-- (this dependency is defined somewhere else) --> [/code] 写到这里,扩展测试中的前三个目的已经基本上实现了。下面帖部分解决1:N中n+1的实现思路(不可实现) 1、改写ArrayList [code="java"] package com.ibatis.sample.comm; import java.lang.reflect.ParameterizedType; public class MyArrayList extends java.util.ArrayList { /** * */ private static final long serialVersionUID = -5105729373313535708L; Class cls = null; @SuppressWarnings("unchecked") public MyArrayList() { super(); //利用反射获得当前类型。 if (cls == null) { ParameterizedType pt = (ParameterizedType) getClass() .getGenericSuperclass(); cls = (Class) pt.getActualTypeArguments()[0]; } } @SuppressWarnings("unchecked") public MyArrayList(int i) { super(i); if (cls == null) { ParameterizedType pt = (ParameterizedType) getClass() .getGenericSuperclass(); cls = (Class) pt.getActualTypeArguments()[0]; } } public MyArrayList(Class cls) { super(); this.cls = cls; } public MyArrayList(Class cls,int i) { super(i); this.cls = cls; } @Override public T get(int index) { //当所取索引=其集合类大小时,就自动在集合类里现添加一个实体类。 if (cls != null && index==this.size()) { try { add((T) cls.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return super.get(index); } } [/code] 改写javaBean [code="java"] /*** * 配置②begin... * 避免N+1(1:N、M:N)问题的例子begin****** * (测试UserAndAddressServiceTest内的testTr1方法时请注释以下部分) // * ****/ private List
addresses = new MyArrayList
(0); public List
getAddresses() { return addresses; } public void setAddresses(List
addresses) { this.addresses.addAll(addresses); } /**配置②end**/ [/code] 配置文件: [code="xml"] <!-- ibatis有个最大的缺陷就是无法解决1:n、M:n的N+1问题(1:1的N+1问题,指南上有解决方案,这里就不重复了。) 对于1:n、M:n的N+1问题本人有个推理解决方案(没有实践过),(以user 1: address N为例)但有几个使用限制。 1.注释UserAndAddress类中的配置①部分。 2.更改数据库连接 3.重写ArrayLIst,重载其中的get方法。见本例中的。com.ibatis.sample.comm.MyArrayList 4.本方法目前只对oracle数据库有效(利用了伪列)。 这里的用户和地址是一对多的关系,用下面getUserAndAddress这种方式取出时会有N+1的问题,现在为了避免N+1换种方法 --> <!-- 双向的1:N、M:N暂时有问题,只能是如指南上所说: 重要提示!目前SQL Map架构无法自动解决resultMap之间的双向关系。 这在处理“父/子” 双向关系的resultMap时尤其要注意。 一个简单的办法是,为其中一种情况再定义一个不装入父对象的resultMap(反之亦然 所以下面这行就不写了 --> <!--<result property="addresses.zipcode" column="id" select="UserAndAddress1.getAddressByUserId" />--> select t.rownums-1 as indexs,t.id,t.name, t.sex,t.addressId,t.address,t.zipcode,t.user_id from (select rownum as rownums, t_user.id,t_user.name, t_user.sex,t_address.id as addressId, t_address.address,t_address.zipcode, t_address.user_id from t_user left outer join t_address on t_user.id =user_id ) t; <!--如有需要,可继续加检索条件。 --> [/code] 至此全文结束。其实本人对ibatis确实不熟,以前用的基本都hb。现在用了ibatis还是觉得ibatis确实有它的可取之处,但烦人的1:N,N:m外加双向关联,解决起来确实太麻烦,不知道各位在日常 的开发应用中是如何实现的。难道都是在需要的时候才去再次检索?或者干脆把性能问题抛开到一边,能实现关联就行??欢迎讨论,有砖尽管拍,但请一定记住了:强烈BS光拍砖不给油(理由)的人~~ ^_^!!! 附件是源码。用myeclipse7.5打开就能用了,加上sql驱动就能用了。包里面有些是struts的包,是本人准备整合用的,但暂时没用上,可以删除。表的话,还请自己建,也就二个表,并且 我还给出了sql语句不是吗(在javabean的注释部分)?做人要勤快。呵呵。。]]> 为什么标签不起作用。谁能告诉我????????
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics