浏览 3777 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (6)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-20
最后修改:2009-07-20
<![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的注释部分)?做人要勤快。呵呵。。]]> 为什么标签不起作用。谁能告诉我???????? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |