<![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:[email protected]
* QQ:254120499
* MSN:[email protected]
* 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:[email protected]
* QQ:254120499
* MSN:[email protected]
* 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:[email protected]
* QQ:254120499
* MSN:[email protected]
* 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:[email protected]
* QQ:254120499
* MSN:[email protected]
* 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:[email protected]
* QQ:254120499
* MSN:[email protected]
* 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的注释部分)?做人要勤快。呵呵。。]]> 为什么标签不起作用。谁能告诉我????????