论坛首页 Java企业应用论坛

用了mybatis,我认为不需要再要写DAO

浏览 66451 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (1)
作者 正文
   发表时间:2011-06-10  
denger 写道
star022 写道
发一下我之前的老帖:http://star022.iteye.com/blog/390549

mybatis 与 hibernate 还是有很大区别的,个人认为 mybatis 完全无须泛型dao。
需要数据库操作只需要写一个 mapper interface 定义一个接口方法+配置一段sql 就OK了,已经是很简单了。无须把问题给复杂化了...


ibatis做泛型,其实很容易的,贴点代码:
public <T> T queryForEntity(String statementName, Class<T> targetClass) {
return (T) queryForObject(statementName);
}

public <T> T queryForEntity(String statementName, Object param, Class<T> targetClass) {
return (T) queryForObject(statementName, param);
}

public <T> List<T> queryForList(String statementName, Class<T> targetClass) {
return queryForList(statementName);
}

public <T> List<T> queryForList(String statementName, Object param, Class<T> targetClass) {
return queryForList(statementName, param);
}

public <T> Page<T> queryForPage(String statementName, Page page, Class<T> targetClass) {
return queryForPage(statementName, page);
}

public <T> Page<T> queryForPage(String statementName, Page page, Object param, Class<T> targetClass) {
return queryForPage(statementName, page, param);
}
0 请登录后投票
   发表时间:2011-06-10   最后修改:2011-06-10
star022 写道
squll369 写道
denger 写道

而且,你说的别人改写 mybatis 的源代码。 其实并不是改写,而是扩展,Mybatis提供了很好的 插件扩展机制,通过使用 拦截器方式对 select 进行拦截,然后在sql上连接 limit 或 rownum 之类的,从而实现物理分页。
另外,也不是别人把 hibernate的dialect包copy过来利用 ,而是参考 hibernate 中 dialect分页的思路,达到目的就是当我换不同数据库的时候(如mysql使用limit,oracle使用rownum)只需要修改修改配置中的 dialect ,也就是 mybatis 的拦截器。 而不是把limit 或 rownum之类的写在配置文件中....

public interface ProductMapper {
	List<Product> selectProducts(Boolean isIndexed, RowBounds pageBounds);
}

对于下面的人,也无须考虑SQL分页的问题:
productMapper.selectProducts(true, new RowBounds(1, 10))

<select id="selectProducts" resultMap="productMap">
  	<![CDATA[
  	select * from tab_product where is_index  = #{isIndexed}
    ]]>
  </select>


配置全局的 plugin:
<plugins>
		<plugin interceptor="com.xxxx.core.plugin.mybatis.SqlPageInterceptor">
			<property name="dialectClass" value="com.xxx.core.plugin.mybatis.MySQLDialect" />
<!-- 如果换成oracle数据库 的,自己定义一个 OracleDialect 即可-->
		</plugin>
	</plugins>

拦截器代码就不贴了,in mybatis official..



这个解决方案确实不错.


看了这个,感觉繁琐了,来说下我去年对ibatis的一个分页改造思路吧,无缝兼容多种数据库:
1.下面的sqlmap配置,其实就是一个分页,也许你会奇怪,没有写rownum 或 limit ,如何做分页呢?继续往下就知道了。
<select id="pageTest" resultMap="AccountResult" parameterClass="Account">
    select * from ACCOUNT  ac where 1= 1
     <dynamic prepend="">
<isNotEmpty prepend="and" property="firstName">
   ac.ACC_FIRST_NAME = #firstName#
   </isNotEmpty>
<isNotEmpty prepend="and" property="emailAddress">
   ac.ACC_EMAIL like #emailAddress#
</isNotEmpty>
</dynamic>
  </select>

2.分页的本质,其实与数据库没有太大的关系,本质是获取一个指定结果集中的“某一段”;
  而因数据库的差异,或者该段结果集的方式不一样,oracle通过rownum来获取,而mysql通过limit,
  因此在做具体的分页的时候,只要两个条件:
    1)。需要那一段结果集,这个可以由运行时上下文获取;
    2)。数据库的类型,这个可以由运行时数据库连接conn获取,方式如下:
         String dataBaseType = conn.getMetaData().getDatabaseProductName().toLowerCase();

   有了1),2)两个条件,后续该怎么做就怎么做吧,就不细说了。。。


引用
1)。需要那一段结果集,这个可以由运行时上下文获取;

大哥,你这个是逻辑分页吧,在运行时的上下文获取?你是将所有数据查出来,在SERVICE层分页?难怪你说
引用
分页的本质,其实与数据库没有太大的关系
 。如果你用的是物理分页,我很想知道你是如何将 limit 或 rownum 连接拼接到这条SQL后面的?
引用
    2)。数据库的类型,这个可以由运行时数据库连接conn获取,方式如下:
         String dataBaseType = conn.getMetaData().getDatabaseProductName().toLowerCase();

这个就觉得更没有必要了。还获取数据库的类型,不知道你的判断数据库类型是硬编码的还是怎么的?既然提供了插件方式,解除分页与数据库的耦合度,即插即用,为什么还要去写 if  else 呢?

PS:
引用
<select id="pageTest" resultMap="AccountResult" parameterClass="Account">
    select * from ACCOUNT  ac where 1= 1

为什么要用 where 1=1呢?mybatis 或 ibatis 都提供了  <where></where>的语句呀,大哥。。

0 请登录后投票
   发表时间:2011-06-10   最后修改:2011-06-10
star022 写道
denger 写道
star022 写道
发一下我之前的老帖:http://star022.iteye.com/blog/390549

mybatis 与 hibernate 还是有很大区别的,个人认为 mybatis 完全无须泛型dao。
需要数据库操作只需要写一个 mapper interface 定义一个接口方法+配置一段sql 就OK了,已经是很简单了。无须把问题给复杂化了...


ibatis做泛型,其实很容易的,贴点代码:
public <T> T queryForEntity(String statementName, Class<T> targetClass) {
return (T) queryForObject(statementName);
}

public <T> T queryForEntity(String statementName, Object param, Class<T> targetClass) {
return (T) queryForObject(statementName, param);
}

public <T> List<T> queryForList(String statementName, Class<T> targetClass) {
return queryForList(statementName);
}

public <T> List<T> queryForList(String statementName, Object param, Class<T> targetClass) {
return queryForList(statementName, param);
}

public <T> Page<T> queryForPage(String statementName, Page page, Class<T> targetClass) {
return queryForPage(statementName, page);
}

public <T> Page<T> queryForPage(String statementName, Page page, Object param, Class<T> targetClass) {
return queryForPage(statementName, page, param);
}

噢,你用的并非 Mapper Interface 方式噢。。。
那么你是如何在 service 层调用dao的?
是: 直接将 BaseDao 注入 service 中, 然后在service中 baseDao.queryForPage("selectAllProduct", new Page(1, 10),  Product.class) ?

如果是这样的话那是否有跨层访问的嫌疑?如果我 mapper xml 中的 statementName 修改了,那我要修改对应的 serivce 层?

0 请登录后投票
   发表时间:2011-06-10  
denger 写道
star022 写道
squll369 写道
denger 写道

而且,你说的别人改写 mybatis 的源代码。 其实并不是改写,而是扩展,Mybatis提供了很好的 插件扩展机制,通过使用 拦截器方式对 select 进行拦截,然后在sql上连接 limit 或 rownum 之类的,从而实现物理分页。
另外,也不是别人把 hibernate的dialect包copy过来利用 ,而是参考 hibernate 中 dialect分页的思路,达到目的就是当我换不同数据库的时候(如mysql使用limit,oracle使用rownum)只需要修改修改配置中的 dialect ,也就是 mybatis 的拦截器。 而不是把limit 或 rownum之类的写在配置文件中....

public interface ProductMapper {
	List<Product> selectProducts(Boolean isIndexed, RowBounds pageBounds);
}

对于下面的人,也无须考虑SQL分页的问题:
productMapper.selectProducts(true, new RowBounds(1, 10))

<select id="selectProducts" resultMap="productMap">
  	<![CDATA[
  	select * from tab_product where is_index  = #{isIndexed}
    ]]>
  </select>


配置全局的 plugin:
<plugins>
		<plugin interceptor="com.xxxx.core.plugin.mybatis.SqlPageInterceptor">
			<property name="dialectClass" value="com.xxx.core.plugin.mybatis.MySQLDialect" />
<!-- 如果换成oracle数据库 的,自己定义一个 OracleDialect 即可-->
		</plugin>
	</plugins>

拦截器代码就不贴了,in mybatis official..



这个解决方案确实不错.


看了这个,感觉繁琐了,来说下我去年对ibatis的一个分页改造思路吧,无缝兼容多种数据库:
1.下面的sqlmap配置,其实就是一个分页,也许你会奇怪,没有写rownum 或 limit ,如何做分页呢?继续往下就知道了。
<select id="pageTest" resultMap="AccountResult" parameterClass="Account">
    select * from ACCOUNT  ac where 1= 1
     <dynamic prepend="">
<isNotEmpty prepend="and" property="firstName">
   ac.ACC_FIRST_NAME = #firstName#
   </isNotEmpty>
<isNotEmpty prepend="and" property="emailAddress">
   ac.ACC_EMAIL like #emailAddress#
</isNotEmpty>
</dynamic>
  </select>

2.分页的本质,其实与数据库没有太大的关系,本质是获取一个指定结果集中的“某一段”;
  而因数据库的差异,或者该段结果集的方式不一样,oracle通过rownum来获取,而mysql通过limit,
  因此在做具体的分页的时候,只要两个条件:
    1)。需要那一段结果集,这个可以由运行时上下文获取;
    2)。数据库的类型,这个可以由运行时数据库连接conn获取,方式如下:
         String dataBaseType = conn.getMetaData().getDatabaseProductName().toLowerCase();

   有了1),2)两个条件,后续该怎么做就怎么做吧,就不细说了。。。


引用
1)。需要那一段结果集,这个可以由运行时上下文获取;

大哥,你这个是逻辑分页吧,在运行时的上下文获取?你是将所有数据查出来,在SERVICE层分页?难怪你说
引用
分页的本质,其实与数据库没有太大的关系
 。如果你用的是物理分页,我很想知道你是如何将 limit 或 rownum 连接拼接到这条SQL后面的?
绝对的物理分页哈,在做分页时,获取运行时需要做分页的sql语句,然后再结合分页参数,组装分页语句,现在贴下组装分页语句的处理器
/**
 * oracle page sql Processor
 */
public class OracleSqlProcessor implements SqlProcessor {
	public String pageSqlProcess(String sql) {
		StringBuilder sBuilder = new StringBuilder();
		sBuilder.append("select * from ( select a.*, rownum row_num from (").append(sql).append(") a where rownum <=  ? ) WHERE row_num > ? ");
		return sBuilder.toString();
	}
}

public class MySqlSqlProcessor implements SqlProcessor {
	// select * from Member limit 1000, 100
	public String pageSqlProcess(String sql) {
		StringBuilder sBuilder = new StringBuilder();
		sBuilder.append("select * from (").append(sql).append(" ) limit ? , ?");

		return sBuilder.toString();
	}
}

引用
    2)。数据库的类型,这个可以由运行时数据库连接conn获取,方式如下:
         String dataBaseType = conn.getMetaData().getDatabaseProductName().toLowerCase();

这个就觉得更没有必要了。还获取数据的类型,不知道你的判断数据库类型是硬编码的还是怎么的?既然提供了插件方式,解除分页与数据库的耦合度,即插即用,为什么还要去写 if  else 呢?
不需要赢编码,数据库的类型,其实jdbc回告诉你一个定值,怎么获取到对应的SqlProcessor,也不需要if..else,由MPA获取就好了;
SqlProcessor sqlProcessor = SQL_PROCESSOR_MAP.get(dataBaseType);


PS:
引用
<select id="pageTest" resultMap="AccountResult" parameterClass="Account">
    select * from ACCOUNT  ac where 1= 1

为什么要用 where 1=1呢?mybatis 或 ibatis 都提供了  <where></where>的语句呀,大哥。。

(1),写上 1=1可能是自己的老习惯,成为了陋习了 ,哈哈。。。这个程序逻辑和性能没什么影响,所以就保留下来了

0 请登录后投票
   发表时间:2011-06-10  
之所下要再后加上 where 1=1 ,有下面的疑虑哈,比如下面的sqlmap配置,如果不加 where 1=1,而直接再后面加上了 and ac.ACC_FIRST_NAME = #xxx# ,这种情况下语句会出错;
你说的<where></where>语句,应该就没有这种担忧~~~

<select id="pageTest" resultMap="AccountResult" parameterClass="Account">
    select * from ACCOUNT  ac where 1= 1
     <dynamic prepend="">
<isNotEmpty prepend="and" property="firstName">
   ac.ACC_FIRST_NAME = #firstName#
   </isNotEmpty>
<isNotEmpty prepend="and" property="emailAddress">
   ac.ACC_EMAIL like #emailAddress#
</isNotEmpty>
</dynamic>
  </select>
0 请登录后投票
   发表时间:2011-06-10  
squll369 写道
supben 写道
啥叫你认为,官方就这么建议的

官方只是推荐使用mapper接口......没有建议说不要写DAO.


那你认为mapper 是什么?
0 请登录后投票
   发表时间:2011-06-10   最后修改:2011-06-10
@star022
1. 你这种方页方式应该是只适用于非 Mapper Interface 方式吧~   Mapper Interface 中是没有任何实现...  要达到这种效果只能使用拦截器器对 interface 中的 method 进行拦截了`

2.
引用
sBuilder.append("select * from (").append(sql).append(" ) limit ? , ?");

请问一下你是不是将 xml 中配置的 sql 连接在这里面了? 比如  xml 的 sql是  select * from members
而最终生成的是:
select * from (select * from members) limit 1, 10
是不是?如果是的话那问题就来了..
0 请登录后投票
   发表时间:2011-06-10  
supben 写道
squll369 写道
supben 写道
啥叫你认为,官方就这么建议的

官方只是推荐使用mapper接口......没有建议说不要写DAO.


那你认为mapper 是什么?

就是呀,很多人感觉天天在写 DAO 完全是为了 DAO 而DAO,现在只是CLASS换了一个后缀(xxDao 改成 xxMapper)的方式了,就说不要DAO了? DAO是什么?数据访问层对象... 
0 请登录后投票
   发表时间:2011-06-10  
supben 写道
squll369 写道
supben 写道
啥叫你认为,官方就这么建议的

官方只是推荐使用mapper接口......没有建议说不要写DAO.


那你认为mapper 是什么?


mapper
名词 n.
制图人
0 请登录后投票
   发表时间:2011-06-10   最后修改:2011-06-10
denger 写道
supben 写道
squll369 写道
supben 写道
啥叫你认为,官方就这么建议的

官方只是推荐使用mapper接口......没有建议说不要写DAO.


那你认为mapper 是什么?

就是呀,很多人感觉天天在写 DAO 完全是为了 DAO 而DAO,现在只是CLASS换了一个后缀(xxDao 改成 xxMapper)的方式了,就说不要DAO了? DAO是什么?数据访问层对象... 



squll369 写道
zhongxuchen 写道
非常荒谬的观念,没有理解分层的意义,其实很多项目中数据库的操作比mybatis还简单,但仍然使用dao,因为service层是业务逻辑,可能调用多个dao来组合业务,如果直接放在service层中,dao的复用就无从谈起


service层一样组装mapper,一样可以复用,没有说取消dao层,其实mapping层就是原来的dao层。


我是说不需要写DAO,不是说不需要DAO层.
只是Mapping Interface + SQL 这个方式, 和以前的DAO interface + DAO Impl + SQL这个方式比较,本来就是不要完全是为了 DAO 而DAO.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics