给自己的每日一句
不从恶人的计谋,不站罪人的道路,不坐亵慢人的座位,惟喜爱耶和华的律法,昼夜思想,这人便为有福!他要像一棵树栽在溪水旁,按时候结果子,叶子也不枯干。凡他所做的尽都顺利。
如何找到孙帅本人
本文内容整理自《孙哥说Mybatis系列视频课程》,老师实力十分雄厚,B站搜孙帅可以找到本人,视频中有老师的微信号。
前言
Mybatis当中可以处理通用的分页,Mybatis开发中传统的分页是怎么接入Mybatis呢?
Mybatis传统开发就:entity、别名、建表、Dao、Mapper、注册、API编程,这七步骤。
Mybatis的分页显然是在Dao和Mapper设计上体现的。在Dao中我们如果引入分页的话,那么就得有页码和每页显示数量了。
UserDao{ List<User> queryAllUsers(int pageIndex,int number); List<User> queryUsersByName(int pageIndex,int number,String name); }
UserDaoMapper.xml <select id = "queryAllUsers"> select * from t_user limit pageindex-1,5<> </select> <select id = "queryUsersByname"> select * from t_user where name like #{name} limit pageindex-1,5 </select>
如果这么玩的话,这个Mapper.xml文件中的Dao层分页查询,得在所有的select标签当中进行添加,这么操作十分冗余。冗余还不是最可怕的,最可怕的是修改起来,那真的是要了命了,假如我们切换了Oracle的数据源,那么select标签中的limit可就不好使了!这样的代码就废了
所以这么干的话,SQL就与厂商特性耦合了。这一块JPA是解决的非常好的,他是通过Dialect方言解决的。
一:分页处理集中处理
解决冗余和解决分页厂商方言问题。这样就适用于我们的拦截器了。我们直接在拦截器当中加上limit 操作即可。
二:不太完善版
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class PageHelperInterceptor1 extends MyMybatisInterceptorAdapter { private static final Logger log = LoggerFactory.getLogger(PageHelperInterceptor1.class); private String queryMethodPrefix; private String queryMethodSuffix; private String databaseType; @Override public Object intercept(Invocation invocation) throws Throwable { if (log.isInfoEnabled()) log.info("----pageHelperInterceptor------"); //获得sql语句 拼接字符串 limit MetaObject metaObject = SystemMetaObject.forObject(invocation); String sql = (String) metaObject.getValue("target.delegate.boundSql.sql"); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("target.delegate.mappedStatement"); String id = mappedStatement.getId(); //基于拦截器propertis灵活掌握是否分页的手段。 if (id.indexOf(queryMethodPrefix) != -1 && id.endsWith(queryMethodSuffix)) { //分页相关的操作封装 对象(vo dto) //获得Page对象 并设置Page对象 totalSize属性 算出总页数 //假设 Page //Page page = new Page(1); //直接通过DAO方法的参数 获得Page对象 //Page page = (Page) metaObject.getValue("target.delegate.parameterHandler.parameterObject"); //通过ThreadLocalUtils Page page = ThreadLocalUtils.get(); //清空一下 //select id,name from t_user 获得 全表有多少条数据 // select count(*) from t_user //select id,name from t_user where name = ?; //select count(*)fromt t_user where name = ? //select id,name from t_user where name = ? and id = ?; String countSql = "select count(*) " + sql.substring(sql.indexOf("from")); //JDBC操作 //1 Connection PreapredStatement Connection conn = (Connection) invocation.getArgs()[0]; PreparedStatement preparedStatement = conn.prepareStatement(countSql); /* preparedStatement.setString(1,?) preparedStatement.setString(2,?);*/ //这里是什么操作呢? //Mybatis当中ParameterHandler提供的处理参数的方法。自动取这个值,自动取组装值即可。 ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("target.delegate.parameterHandler"); parameterHandler.setParameters(preparedStatement); 9jh ResultSet resultSet = preparedStatement.executeQuery(); if(resultSet.next()){ page.setTotalSize(resultSet.getInt(1)); } //page.setTotalSize(); ` //做一个判断 如果当前是MySQL 如果是Oracle.... //if databaseType == "oracle" or "mysql" String newSql = sql + " limit "+page.getFirstItem()+","+page.getPageCount(); metaObject.setValue("target.delegate.boundSql.sql", newSql); } return invocation.proceed(); } @Override public void setProperties(Properties properties) { this.queryMethodPrefix = properties.getProperty("queryMethodPrefix"); this.queryMethodSuffix = properties.getProperty("queryMethodSuffix"); } }
/** * 目的 封装分页相关操作 * pageIndex 页号 * web 前端传递 * pageCount 每页显示多少条 * 写死 3 * totalSize 总共有多少条 * count(*) 算出来 * <p> * * pageSize 一共有多少页 * totalSize / pageCount * 6 / 3 = 2 * 7 / 3 = 3 */ public class Page { private Integer pageIndex; private Integer pageCount; //查询数据库的 //sql语句相关 //计算在哪里完成? 拦截器操作 private Integer totalSize; private Integer pageSize; public Page(Integer pageIndex) { this.pageIndex = pageIndex; this.pageCount = 5; } public Page(Integer pageIndex, Integer pageCount) { this.pageIndex = pageIndex; this.pageCount = pageCount; } public Integer getPageIndex() { return pageIndex; } public void setPageIndex(Integer pageIndex) { this.pageIndex = pageIndex; } public Integer getPageCount() { return pageCount; } public void setPageCount(Integer pageCount) { this.pageCount = pageCount; } public Integer getTotalSize() { return totalSize; } public void setTotalSize(Integer totalSize) { this.totalSize = totalSize; if (totalSize % pageCount == 0) { this.pageSize = totalSize / pageCount; } else { this.pageSize = totalSize / pageCount + 1; } } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } // limit getFirstItem,pageSize; public Integer getFirstItem() { return pageIndex - 1; } }
@Test public void test2() throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); UserDAO userDAO1 = sqlSession1.getMapper(UserDAO.class); UserDAO userDAO2 = sqlSession2.getMapper(UserDAO.class); UserDAO userDAO3 = sqlSession3.getMapper(UserDAO.class); /* List<User> users1 = userDAO1.queryAllUsersByPage();//--- ms ---> cache for (User user : users1) { System.out.println("user = " + user); }*/ User user1 = userDAO1.queryUserById(4); //---ms ---> cache sqlSession1.commit(); //userDAO1.queryAllUsers(); System.out.println("-----------------------------------------"); /*List<User> users2 = userDAO2.queryAllUsersByPage(); for (User user : users2) { System.out.println("user = " + user); }*/ sqlSession2.commit(); System.out.println("---------------------------------2人 --------"); // /* User user = new User(4, "xiaowb"); userDAO3.update(user); sqlSession3.commit();*/ }
三:如何获取Page对象
如何获取Page对象的获取和封装,这个位置应该发生在什么位置呢?应该在Controller层当中。服务器解析请求数据的时候会在Controller当中进行解析。controller会通过Request对象获取到页号。并在Controller当中封装成page对象。把Page对象放置到ThreadLocal当中一直送到Dao当中,也可以直接在Dao层当中基于参数传递过来即可。
直接使用ThreadLocal会更好。
1:Tomcat处理一个请求
Tomcat接收到一个请求之后,会从线程池大当中去取出来一个线程来处理请求。请求完毕之后会将线程放回到线程池当中。我们把Page对象放到线程的ThreadLocal当中即可。
ThreadLocal是sun公司为我们提供的一个工具,把一个对象存储在当前线程中。可以准确的获取当前线程,也可以知道对象存储在线程的什么位置。ThreadLocal在Thread当中以自己作为Key,存储在Map中。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocal就是操作了当前Thread当中的Map,ThreadLocal对象本身作为Map的key,value是存储在其中的值,神奇的是,Thread中的Map是定义在ThreadLocal当中的static class.
2:问题集锦
1):ThreadLocal存在内存泄露
内存泄露核心就是,你所存储的东西太多了,导致你的内存不够用了,所以Dao用完了,我得把Map中的数据给清掉。用完之后就把他干掉。
2):ThreadLocal到底把数据存储在了哪里?
Thread当中。
Connection、SqlSession、Page这些对象经常会存储到Thread当中。
3):什么时候调用ThreadLocal把Page存储在线程中?
Controller当中。当然,我们认为这个还不够早,我们觉得在filter过滤器当中就可以执行这个操作了。
四:Mybatis拦截器中操作Sql的工具?
1:SQLparser解析器
/** * 用于测试:jSqlparser */ @Test public void testSQLParser() throws JSQLParserException { CCJSqlParserManager parserManager = new CCJSqlParserManager(); Select select = (Select) parserManager.parse(new StringReader("select id,name from t_user where name = 'suns' ")); PlainSelect selectBody = (PlainSelect) select.getSelectBody(); //FromItem table = selectBody.getFromItem(); //System.out.println("table = " + table); /* Expression where = selectBody.getWhere(); System.out.println("where = " + where);*/ /* List<SelectItem> selectItems = selectBody.getSelectItems(); for (SelectItem selectItem : selectItems) { System.out.println("selectItem = " + selectItem); }*/ }
@Test public void testSQLParser1() throws JSQLParserException { CCJSqlParserManager parserManager = new CCJSqlParserManager(); Update update = (Update) parserManager.parse(new StringReader("update t_user set name='suns',password='12345' where id=1 ")); /*Table table = update.getTable(); System.out.println("table = " + table);*/ List<Column> columns = update.getColumns(); for (Column column : columns) { System.out.println(column); } List<Expression> expressions = update.getExpressions(); for (Expression expression : expressions) { System.out.println(expression); } }