`

mybatis直接执行sql语句后续之一

 
阅读更多

在上一篇文章中,我们提到了让mybatis直接执行sql语句。

http://xiabin1235910-qq-com.iteye.com/blog/1748886

 

接下来介绍在上一篇文章的基础上,我们如何使用mybatis,以及在编程时,应该注意的事项:

1. 命名规约:




 
 配置文件的namespace和Box类的路径要一致,我们稍后将会用反射的方式,将Box和BoxMapper串接起来。

 

在项目中采用的普遍做法是,在dao层建立一个泛型基类,这个泛型基类提供一个公用方法,将实体和xml文件对应起来。如图:



 

好的,介绍一下DaoBase泛型类,首先要将实体和xml串联起来。

public interface DaoSupport<T> {
	/**
	 * 添加对象
	 * @param t
	 */
	public abstract void add(T t);
}

 

public class DaoBase<T> implements DaoSupport<T> {
	@Override
	public void add(T o) {
		try {
			SessionManage.dealSession(SessionMethod.INSERT,
					getDealMethod(SessionMethod.INSERT).toString(), o);
		} catch (SQLException e) {
			System.out.println("事务异常");
			e.printStackTrace();
		}
	}

        private StringBuilder getDealMethod(SessionMethod sm) {
		Class<T> c = getDetailClass();
		StringBuilder queryMapper = new StringBuilder(c.getName());
		queryMapper.append(".");
		if (sm.equals(SessionMethod.DELETE)) {
			queryMapper.append(SessionMethod.DELETE.getName());
		} else if (sm.equals(SessionMethod.UPDATE)) {
			queryMapper.append(SessionMethod.UPDATE.getName());
		} else {
			queryMapper.append(SessionMethod.INSERT.getName());
		}
		queryMapper.append(c.getSimpleName());
		return queryMapper;
	}

        private Class<T> getDetailClass() {
		Class<T> en;
		Class c = this.getClass();
		// System.out.println(c.getName());
		ParameterizedType ptype = null;
		do { // 遍历所有超类,直到找泛型定义
			try {
				ptype = (ParameterizedType) c.getGenericSuperclass();
			} catch (Exception e) {
			}
			c = c.getSuperclass();
			// System.out.println("super class:" + ptype);
		} while (ptype == null && c != null);
		if (ptype == null) {
			System.out.println("子类中没有定义泛型的具体类型");
		}
		en = (Class<T>) ptype.getActualTypeArguments()[0];
		// System.out.println(en.getSimpleName());
		return en;
	}
}

 其核心思想,就是在泛型类中通过查找出<T> 的 T 的类型,取得T 类的路径, 在sessionManage中将T和 T对应的xml 关联起来,以便完成添加对象的方法。sessionManage的代码,如下:

public class SessionManage {
	static TransactionFactory transactionFactory = new JdbcTransactionFactory();

        public static void dealSession(SessionMethod sm, String queryMethod, Object param) throws SQLException {
		SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession();
		Transaction transaction = transactionFactory.newTransaction(
				session.getConnection(), false);		
		try {
			Method m = null;
			m = session.getClass().getDeclaredMethod(sm.getName(), String.class ,Object.class);
			if(param instanceof List) {
				for(Object o : (List)param) {
					m.invoke(session, queryMethod, o);
				}
			} else {
				m.invoke(session, queryMethod, param);
			}
//			int a = 45/0;  //生成错误判断是否回滚
			//事务提交
			transaction.commit();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (Exception e) {
			//事务回滚
			transaction.rollback();
			System.out.println("事务回滚");
			e.printStackTrace();
		} finally {
			transaction.close();
                        session.close();
		}
	}

}

其核心思想,就是通过反射,将mybatis的session.insert()   session.update()   session.delete() 等方法抽象成一个方法。 

 

附:枚举类sessionMethod代码:

public enum SessionMethod {
	INSERT {
		@Override
		public String getName() {
			return "insert";
		}
	}, 
	UPDATE {
		@Override
		public String getName() {
			return "update";
		}
	}, 
	DELETE {
		@Override
		public String getName() {
			return "delete";
		}
	};
	
	public abstract String getName();
}

 获取sessionFactory方法:

 

public class MyBatisUtil {
	private static SqlSessionFactory sqlSessionFactory = null;
	
	public synchronized static SqlSessionFactory getSqlSessionFactory() {
		if(sqlSessionFactory == null) {
			String resource = "mybatis-configuration.xml";
			Reader reader = null;
			try {
				reader = Resources.getResourceAsReader(resource);
			} catch (IOException e) {
				e.printStackTrace();
			}
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		}
		return sqlSessionFactory;
	}
}
 

以上,仅仅完成了万里长征的第一步,还有更加复杂的查询逻辑等着我们去处理。

这才是我们需要处理的核心内容,也是难点。

 

--------------------------------------------------------------------------------------------------------------------------------------------------

 

相信有了上面的基础,对于我们理解下面的内容有很大帮助。

我们在用自己编写的分页框架,查询语句时,往往需要我们自己去维护这段代码,这就需要我们让mybatis直接执行sql语句。 配置的话 在开头我已经讲过,下面着重讲解使用方式:

 

首先,在做项目时,我们会碰到各种各样的需求,使用软删除,复杂的sql查询语句,表间级联关系查询等等,

所以有必要采用抽象的方式,整合它们。  我们先从DaoBase这个基类入手。

 

public class DaoBase<T> implements DaoSupport<T> {

        @Override
	public QueryResult<T> getScrollData(long startIndex, long maxResult,
			String whereSql, Object[] params,
			LinkedHashMap<String, String> orderBy) {
		return getAbstractScrollData(startIndex, maxResult, whereSql, params,
				orderBy, false);
	}

        protected QueryResult<T> getAbstractScrollData(long firstIndex,
			long maxResult, String whereSql, Object[] params,
			LinkedHashMap<String, String> orderBy, boolean delete) {
		return getManyToManyScrollData(firstIndex, maxResult, null, whereSql,
				params, orderBy, delete);
	}

	@Override
	public QueryResult<T> getScrollData(long startIndex, long maxResult,
			String tableMapping, String whereSql, Object[] params) {
		return getManyToManyScrollData(startIndex, maxResult, tableMapping,
				whereSql, params, null, false);
	}

        protected QueryResult<T> getManyToManyScrollData(long firstIndex,
			long maxResult, String tableMapping, String whereSql,
			Object[] params, LinkedHashMap<String, String> orderBy,
			boolean delete) {
		Class<T> c = getDetailClass();
		String classNameAll = c.getName();
		String className = c.getSimpleName();
		StringBuilder sql = getSQL(tableMapping, whereSql, params, orderBy,	className, delete);
		StringBuilder sqlCounts = getCountsSql(tableMapping, whereSql, params, className, delete);
		sql.append(" ").append("limit ").append(firstIndex).append(", ").append(maxResult);
		return getQuertResult(classNameAll, sql.toString(),	sqlCounts.toString());

	}

        private StringBuilder getSQL(String tableMapping, String whereSql,
			Object[] params, LinkedHashMap<String, String> orderBy,
			String className, boolean delete) {
		StringBuilder sql = new StringBuilder("select n.* from ");
		sql.append(className.toLowerCase()).append(" n ")
				.append(tableMapping == null ? "" : tableMapping + " ")
				.append("where n.deleteflag=").append(delete ? "1" : "0");
		if (whereSql != null) {
			sql.append(buildWhere(whereSql, params));
		}
		if (orderBy != null) {
			sql.append(" ").append(buildOrderBy(orderBy));
		}
		return sql;
	}

	private QueryResult<T> getQuertResult(String classNameAll, String sql,
			String sqlCounts) {
		QueryResult<T> queryResult = new QueryResult<T>();
		if (sql != null) {
			List<T> tlist = SessionManage.getRecordsBySQL(classNameAll, sql);
			queryResult.setResultList(tlist);
		}

		// 分页的结果集需要统计总记录数,否则,不统计
		if (sqlCounts != null) {
			List<String> counts = new ArrayList<String>();

			counts = SessionManage.getRecordsOfColumnBySQL(classNameAll,
					sqlCounts);
			if (counts != null && counts.size() > 0) {
				queryResult.setTotalRecord(Long.valueOf(counts.get(0)));
			} else {
				queryResult.setTotalRecord(0L);
			}
		}

		return queryResult;
	}

}
 

 

这里的Daobase基类,主要完成了向上层暴露查询分页方法的底层实现。 QueryResult是我们实现的一个分页器,里面存放主要的内容就是 查询集合和总共的集合数量。

 

这里介绍一下两个暴露的方法:

 

getScrollData(long startIndex, long maxResult,
			String whereSql, Object[] params,
			LinkedHashMap<String, String> orderBy)

 

 

 

getScrollData(long startIndex, long maxResult,
			String tableMapping, String whereSql, Object[] params)

 

 

稍加比较,就会发现,第二个方法加入了一个tablemapping参数, 这个参数主要是为了实现表间级联查询所设计的。 

两个方法公用的参数whereSql和params 是为了拼凑where语句使用的。

最后拼凑出来的整体效果如下: select n.* from box n where n.boxtype='xxx' orderby boxid ASC;

 

介绍一下,我们如何使用这个这两个方法,我们在servlet中使用,代码如下:

 

private void getBoxList(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		String pageOfString = request.getParameter("page");
		int page;
		if(pageOfString == null || "".equals(pageOfString)) {
			page = 1;
		} else {
			page = Integer.valueOf(pageOfString);
		}
		int maxResult = 10;
		
		String selectName = request.getParameter("selectName");
		String shiptypeid = request.getParameter("shiptypeid");
		
		QueryResult<Box> qr = new QueryResult<Box>();
		StringBuilder wheresql = new StringBuilder("1=1");
		List<Object> olist = new ArrayList<Object>();
                LinkedHashMap<String, String> orderby = new LinkedHashMap<String,String>();
                orderby.put("boxid", "ASC");
		if (selectName != null && !selectName.equals("")) {
			wheresql.append(" and n.boxtype like ?");
			olist.add("%" + selectName + "%");
			request.setAttribute("selectName", selectName);
		}
		if(shiptypeid != null && !"".equals(shiptypeid)) {
			wheresql.append(" and n.shiptypeid=?");
			olist.add(shiptypeid);
			request.setAttribute("shiptypeid", shiptypeid);
		}
		
		PageView<Box> pageView = new PageView<Box>(maxResult, page);
		
		qr = bs.getScrollData(pageView.getFirstResult(),
				pageView.getMaxresult(), wheresql.toString(),
				olist.toArray(), orderby);
		
		pageView.setQueryResult(qr);
		request.setAttribute("pageView", pageView);
		doBrowse(request, response, PageManage.addOtherPrefixAddress("boxList.jsp"));
	}
 没错,我们采用了很像hibernate的where语句写法,完整的sql语句 :

 

1=1 and n.boxtype like ? and n.shiptypeid=?       在olist加入参数的顺序必须和问号的顺序一致

底层包装后的显示 select n.* from box n where 1=1 and n.boxtype like ? and n.shiptypeid=? orderby boxid ASC

下面我们的考虑就是,如何将问号替换成参数,而且字符串和数字作为参数的形式还不一样,一个加' ' , 一个不加。

我们把这项职责还是交给DaoBase,代码如下:

private StringBuilder buildWhere(String whereSql, Object[] params) {
		StringBuilder where = new StringBuilder();
		where.append(" and ");
		int temp = whereSql.lastIndexOf("?");
		// 表达式中不含等号
		if (temp == -1) {
			where.append(whereSql);
		} else {
			if (whereSql != null && params.length > 0) {

				String[] a = whereSql.split("[?]");
				int para = a.length - 1;
				// "?"不是最后一个字符
				if (temp < whereSql.length() - 1) {
					para = para - 1;
				}
				for (int i = 0; i < a.length; i++) {
					where.append(a[i]);
					if (i <= para) {
						if (params[i] instanceof String
								|| params[i] instanceof Timestamp
								|| params[i] instanceof Date) {
							where.append("'").append(params[i]).append("'");
						} else {
							where.append(params[i]);
						}
					}
				}
			}
		}
		return where;
	}

 

相信大家应该明白了,这是怎么一回事儿了。 对于刚接触mybatis的朋友也没关系,我在此上传例子工程,供大家学习。由于个人精力有限,肯定多有设计不足之处,欢迎大家指正。

  • 大小: 18 KB
  • 大小: 62.5 KB
  • 大小: 42.1 KB
分享到:
评论

相关推荐

    MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据

    MyBatis的动态SQL功能是其强大之处之一,它极大地简化了SQL语句的编写过程,提高了开发效率和代码质量。通过灵活运用各种动态SQL元素,开发者可以轻松构建出复杂且高效的数据库操作逻辑。然而,在享受便利的同时,也...

    mybatis导出xml文件(只支持mysql数据库)

    MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。...这种工具通常基于数据库表结构,自动生成包含CRUD操作的SQL语句,并将这些映射关系写入XML文件,便于后续的MyBatis整合和数据库交互。

    MyBatis防止批量更新1

    MyBatis 是一个流行的持久层框架,它提供了插件机制来拦截 SQL 操作,以便于开发者可以在执行 SQL 语句前进行预处理或后续处理。本文将详细介绍 MyBatis 插件机制的实现原理和应用场景。 MyBatis 插件机制的实现...

    MyBatis的动态SQL详解[借鉴].pdf

    MyBatis动态SQL是其强大特性之一,它允许开发者在XML映射文件中编写类似Java代码的逻辑,极大地提高了SQL查询的灵活性。动态SQL是基于OGNL(Object-Graph Navigation Language)表达式实现的,使得在SQL语句中可以轻松...

    如何批量测试Mybatis项目中的Sql是否正确详解

    总结来说,批量测试Mybatis项目中的SQL语句需要理解Mybatis的运行机制,利用反射技术动态执行Mapper接口中的方法,并记录执行结果。通过这种方法,开发者可以在数据库环境变化或SQL调整时快速定位和解决问题,确保...

    day66-mybatis面试题

    MyBatis相对简单,对SQL语句的控制更直接。Hibernate通常生成的SQL语句更高效,但MyBatis允许开发者自己编写更优化的SQL语句。 3. MyBatis的缓存机制:MyBatis有一级缓存(SqlSession级别的)和二级缓存...

    MyBatis3官方中文文档

    实际上,MyBatis采用了一个插件系统,这些插件可以修改或者拦截方法调用,例如SQL语句的执行,可以在方法调用前后添加很多额外的功能。MyBatis的插件可以用来实现分页、性能监控、数据加密、缓存等功能。 另外,...

    Mybatis讲义

    MyBatis 在执行 SQL 查询时,会先通过配置文件或者注解读取 SQL 语句,然后通过 JDBC API 向数据库发送 SQL 命令并获取结果集。接下来,MyBatis 会根据配置的映射规则将结果集中的数据转换为 Java 对象。 #### 五、...

    mybatis入门项目代码

    MyBatis允许开发者编写SQL语句,将数据库查询与应用程序逻辑紧密地结合起来,提高了开发效率。 【描述】提到的链接是一个CSDN博客文章,它详细介绍了如何构建一个MyBatis入门项目。在这个过程中,开发者通常会经历...

    手撕Mybatis源码,自己动手实现Mybatis

    9. **XML配置和注解映射**: Mybatis支持两种方式来定义SQL语句:XML映射文件和注解。XML文件通常位于resources目录下,而注解可以直接写在Mapper接口的方法上。 10. **动态SQL**: Mybatis 的一大亮点是动态SQL,...

    《一头扎进MyBatis3》第一讲 MyBatis 版 HelloWorld 实现

    它将SQL语句与Java代码分离,提供了一种基于XML或注解方式的映射机制,使得开发者能够更专注于SQL语句的编写,而不必关心那些繁琐的JDBC操作。 在创建Java工程的过程中,我们需要配置以下基本元素: 1. **pom.xml**...

    mybatis3--1.入门小练习

    9. **测试**:编写 JUnit 测试用例来验证数据访问功能的正确性,包括增删查改等操作,确保 MyBatis 配置和 SQL 语句的正确执行。 通过这个入门小练习,你可以熟悉 MyBatis 的基本使用,理解其核心概念,并能动手...

    mybatis-day01

    MyBatis允许开发者编写SQL语句,将它们映射到Java方法上,提供了更加灵活和直接的数据访问方式,避免了传统的数据访问框架中的大量对象关系映射(ORM)工作。 【描述】"用在持久层,还可以再增加或者修改的时候,从...

    mybatis实现问题-回复1

    MyBatis是一个轻量级的持久层框架,它允许开发者将SQL语句与Java代码分离,从而提高了开发效率和代码的可维护性。在MyBatis中,执行器(Executor)是一个关键组件,它在整个SQL执行过程中起着总指挥的作用。 1. **...

    springbatch mybatis 代码

    1. **灵活的映射**:MyBatis 提供XML或注解方式来配置SQL语句,直接关联POJO(Plain Old Java Object),避免了复杂的实体映射。 2. **动态SQL**:MyBatis 支持在SQL语句中使用条件、循环等复杂逻辑,极大地提高了...

    mybatis初识

    3. **接口绑定**:MyBatis允许你定义一个接口,然后将接口的方法与映射的SQL语句关联起来。这样,你在业务逻辑中只需要调用接口方法,MyBatis会自动执行相应的SQL并处理结果。 4. **参数映射**:MyBatis能够自动将...

    project_mybatis01.rar

    MyBatis的动态SQL功能强大,可以在XML映射文件中使用`if`、`choose`(when/otherwise)、`where`、`trim`、`foreach`等元素来构造动态的SQL语句,灵活应对复杂的业务需求。 7. **事务管理** MyBatis支持编程式和...

    mybatis入门测试dao资源

    通过MyBatis的XML配置文件或注解,我们可以定义SQL语句,并在DAO接口中声明对应的方法。这样,当调用这些方法时,MyBatis会自动执行相应的SQL操作。 描述中的 "http://magic_duck.oschina.io/ 所需的博客资源" 暗示...

Global site tag (gtag.js) - Google Analytics