物理分页

这次我们运用Mybatis拦截器来实现物理分页,后面会运用动态sql来实现,或者运用Map/CollectionUtils/StringUtils编写工具类来实现。oracle是运用的rownum,mysql是运用的limit offset,pagesize。代码中有大量注释,可以参考Mybatis基本原理一起阅读。后面,我们会根据一些实际开发需要,把物理分页功能的代码封装成jar包,以后直接调用就好了,比如Mybatis+Spring3的运行环境,可以采用Mybatis动态sql来实现分页,也可以采用Mybatis插件。

1、项目备份。一定要勤于备份。我们永远不知道我们什么时候,会把早上还跑得通的一个项目,到晚上都还找不到哪里被玩坏了。是的,我们在编写控制层的时候,交互使用了struts2和springmvc,想看看他们之间的差异,然后,就迷糊了,由于忘记备份struts2版本的,到了晚上就只好还原到昨天的去了。很晕很晕。

2、Mybatis插件类,实现拦截器,实现sql重写。每一个拦截器必须实现三个方法。

 package com.dyl.util;

 import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties; import javax.xml.bind.PropertyException; import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* 拦截器签名
* 要拦截的目标类型是StatementHandler(注意:type只能配置成接口类型),拦截的方法是,名称为prepare,参数为Connection类型的方法。
* @author dyl
* @date 2014-7-12
*/
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PagePlugin implements Interceptor {
/**
* Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的)。
*
* 每一个拦截器都必须实现的三个方法。
* (1)Object intercept(Invocation ivk),实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,
* 也就是调用下一个拦截器拦截目标方法。
* (2)Object plugin(Object target),用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this)来完成的,
* 把目标target和拦截器this传给了包装函数。
* (3)void setProperties(Properties p),设置额外的参数,参数配置在拦截器的Properties节点里。
*
* Mybatis配置的插件,运行时发生。
* (1)所有可能被拦截的处理类都会生成一个代理。
* (2)处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法。
* (3)执行插接中的拦截方法后,推进目标的执行。
*/
private static String dialect = "";
private static String pageSqlId = "";
/*
* intercept的实现
* 实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
* @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
*/
@SuppressWarnings("unchecked")
public Object intercept(Invocation ivk) throws Throwable {
/**
* StatementHandler的默认实现类是RoutingStatementHandler,因此拦截的实际对象是它。
* RoutingStatementHandler的主要功能是分发,它根据配置Statement类型创建真正执行数据库操作的StatementHandler,并将其保存到delegate属性里。
* 由于delegate是一个私有属性并且没有提供访问它的方法,因此需要借助ReflectHelper的帮忙。
* 通过ReflectHelper的封装后我们可以轻易的获得想要的属性。
*/
if (ivk.getTarget() instanceof RoutingStatementHandler) {
RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement"); // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql。
if (mappedStatement.getId().matches(pageSqlId)) {
BoundSql boundSql = delegate.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
if (parameterObject == null) {
throw new NullPointerException("parameterObject error");
} else {
Connection connection = (Connection) ivk.getArgs()[0];
String sql = boundSql.getSql();
// 记录总记录数
String countSql = "select count(0) from (" + sql + ") myCount";
// System.out.println("总数sql 语句:" + countSql);
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), parameterObject);
// 重设分页参数里的总页数等
setParameters(countStmt, mappedStatement, countBS, parameterObject); ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close(); // 分页参数作为参数对象parameterObject的一个属性
PageInfo page = null;
if (parameterObject instanceof PageInfo) {
page = (PageInfo) parameterObject;
page.setTotalResult(count);
} else if (parameterObject instanceof Map) {
Map<String, Object> map = (Map<String, Object>) parameterObject;
page = (PageInfo) map.get("page");
if (page == null)
page = new PageInfo();
page.setTotalResult(count);
} else {
Field pageField = ReflectHelper.getFieldByFieldName(parameterObject, "page");
if (pageField != null) {
page = (PageInfo) ReflectHelper.getValueByFieldName(parameterObject, "page");
if (page == null)
page = new PageInfo();
page.setTotalResult(count);
ReflectHelper.setValueByFieldName(parameterObject, "page", page);
} else {
throw new NoSuchFieldException(parameterObject.getClass().getName());
}
}
// 重写sql
String pageSql = generatePageSql(sql, page);
System.out.println("page sql:" + pageSql);
ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql);
}
}
}
// 将执行权交给下一个拦截器,完成调用链的推进。
return ivk.proceed();
}
/**
* 重设分页参数里的总页数等
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject)
throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)
&& boundSql.hasAdditionalParameter(prop.getName())) {
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null) {
value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
}
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement "
+ mappedStatement.getId());
}
typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
}
}
}
}
/**
* sql重写,在原始的sql语句上加入分页的参数,目前支持mysql和oracle两种数据库的分页。
* @param sql
* @param page
* @return
*/
private String generatePageSql(String sql, PageInfo page) {
if (page != null && (dialect != null || !dialect.equals(""))) {
StringBuffer pageSql = new StringBuffer();
//StringBuilder pageSql = new StringBuilder();
if ("mysql".equals(dialect)) {
//pageSql=generatePageSqlForMysql(sql,page);
pageSql.append(sql);
pageSql.append(" limit " + page.getCurrentResult() + "," + page.getShowCount());
} else if ("oracle".equals(dialect)) {
//pageSql = generatePageSqlForOracle(sql, page);
pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
pageSql.append(sql);
pageSql.append(") tmp_tb where ROWNUM<=");
//pageSql.append(page.getCurrentResult() + page.getShowCount());
pageSql.append(page.getCurrentPage()*page.getShowCount());
pageSql.append(") where row_id>");
//pageSql.append(page.getCurrentResult());
pageSql.append((page.getCurrentPage() - 1) * page.getShowCount());
}
return pageSql.toString();
} else {
return sql;
}
}
/**
* mysql的分页实现
* @param sql
* @param page
* @return
*/
public StringBuilder generatePageSqlForMysql(String sql, PageInfo page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
pageSql.append(sql);
pageSql.append(" limit " + beginrow + "," + page.getShowCount());
return pageSql;
}
/**
* oracle的分页实现
* @param sql
* @param page
* @return
*/
public StringBuilder generatePageSqlForOracle(String sql, PageInfo page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
String endrow = String.valueOf(page.getCurrentPage() * page.getShowCount());
pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
pageSql.append(sql);
pageSql.append(" ) temp where rownum <= ").append(endrow);
pageSql.append(") where row_id > ").append(beginrow);
return pageSql;
}
/**
* plugin的实现
* 用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this)来完成的,把目标target和拦截器this传给了包装函数。
*/
public Object plugin(Object target) {
// 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数。
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
/**
* 设置额外的参数,参数配置在拦截器的Properties节点里。
*/
public void setProperties(Properties p) {
dialect = p.getProperty("dialect");
if (dialect == null || dialect.equals("")) {
try {
throw new PropertyException("dialect property is not found!");
} catch (PropertyException e) {
e.printStackTrace();
}
}
pageSqlId = p.getProperty("pageSqlId");
if (dialect == null || dialect.equals("")) {
try {
throw new PropertyException("pageSqlId property is not found!");
} catch (PropertyException e) {
e.printStackTrace();
}
}
}
}

3、页面实体类,把页面相关属性封装成一个类,方便数据传输。----------------------------上面的属性是用于拦截器的,下面的属性是用于动态sql和工具类的。

 package com.dyl.util;

 import java.io.Serializable;
import java.util.List;
/**
* 页面
* @author dyl
* @date 2014-7-12
*/
public class PageInfo implements Serializable {
private static final long serialVersionUID = -7404704191471426656L;
// pagesize,每一页显示多少
private int showCount = 3;
// 总页数
private int totalPage;
// 总记录数
private int totalResult;
// 当前页数
private int currentPage;
// 当前显示到的ID,在mysql limit 中就是第一个参数。
private int currentResult;
private String sortField;
private String order; // ---------------------------------------------------------------------- // 分页结果
private List<?> result; // 查询条件
private List<?> conditions; // 开始页码
private int start; // 每页多少
private int limit; // 成功与否
private boolean success; // 总记录数
private int totalProperty; public int getTotalProperty() {
return totalProperty;
} public void setTotalProperty(int totalProperty) {
this.totalProperty = totalProperty;
} public boolean isSuccess() {
return success;
} public void setSuccess(boolean success) {
this.success = success;
} public int getStart() {
return start;
} public void setStart(int start) {
this.start = start;
} public int getLimit() {
return limit;
} public void setLimit(int limit) {
this.limit = limit;
} public List<?> getConditions() {
return conditions;
} public void setConditions(List<?> conditions) {
this.conditions = conditions;
} public List<?> getResult() {
return result;
} public void setResult(List<?> result) {
this.result = result;
} public int getShowCount() {
return showCount;
} public void setShowCount(int showCount) {
this.showCount = showCount;
} public int getTotalPage() {
return totalPage;
} public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
} public int getTotalResult() {
return totalResult;
} public void setTotalResult(int totalResult) {
this.totalResult = totalResult;
} public int getCurrentPage() {
return currentPage;
} public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
} public int getCurrentResult() {
return currentResult;
} public void setCurrentResult(int currentResult) {
this.currentResult = currentResult;
} public String getSortField() {
return sortField;
} public void setSortField(String sortField) {
this.sortField = sortField;
} public String getOrder() {
return order;
} public void setOrder(String order) {
this.order = order;
}
}

4、delegate是一个私有属性并且没有提供访问它的方法,因此我们需要写一个工具类来访问它,这个工具类封装了和它相关的一些方法。

 package com.dyl.util;

 import java.lang.reflect.Field;

 /**
* 辅助类
*
* StatementHandler的默认实现类是RoutingStatementHandler,因此拦截的实际对象是它。
* RoutingStatementHandler的主要功能是分发,它根据配置Statement类型创建真正执行数据库操作的StatementHandler,并将其保存到delegate属性里。
* 由于delegate是一个私有属性并且没有提供访问它的方法,因此需要借助ReflectHelper的帮忙。
* 通过ReflectHelper的封装后我们可以轻易的获得想要的属性。
*
* @author dyl
* @date 2014-07-12
*/
public class ReflectHelper {
public static Field getFieldByFieldName(Object obj, String fieldName) {
// 从拦截器的注解中获取拦截的类名
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
}
}
return null;
} /**
* Obj fieldName 获取属性值
*
* @param obj
* @param fieldName
* @return
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static Object getValueByFieldName(Object obj, String fieldName) throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = getFieldByFieldName(obj, fieldName);
Object value = null;
if (field != null) {
if (field.isAccessible()) {
value = field.get(obj);
} else {
field.setAccessible(true);
value = field.get(obj);
field.setAccessible(false);
}
}
return value;
} /**
* Obj fieldName 设置属性值
*
* @param obj
* @param fieldName
* @param value
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void setValueByFieldName(Object obj, String fieldName, Object value) throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
field.set(obj, value);
} else {
field.setAccessible(true);
field.set(obj, value);
field.setAccessible(false);
}
}
}

5、休息一下,我们测试一下性能。在插件类中,重写sql方法,我们运用了StringBuffer、StringBuilder类。根据单线程或多线程的场景使用StringBuilder或StringBuffer。

 package com.dyl.test;

 import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* String,StringBuffer,StringBuilder的区别。
* String,字符串常量,不可变对象。
* StringBuffer,字符串变量,线程安全。
* StringBuilder,字符串变量,非线程安全。
* 性能:String<StringBuffer<StringBuilder。
* @author Administrator
*
*/
public class StringBuilderTest {
private static final String base = " base string. ";
private static final int count = 2000000; public static void stringTest() {
long begin, end;
begin = System.currentTimeMillis();
String test = new String(base);
for (int i = 0; i < count / 100; i++) {
test = test + " add ";
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used String. ");
} public static void stringBufferTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuffer test = new StringBuffer(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuffer. ");
} public static void stringBuilderTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuilder test = new StringBuilder(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuilder. ");
} public static String appendItemsToStringBuiler(List list) {
StringBuilder b = new StringBuilder();
for (Iterator i = list.iterator(); i.hasNext();) {
b.append(i.next()).append(" ");
}
return b.toString();
} public static void addToStringBuilder() {
List list = new ArrayList();
list.add(" I ");
list.add(" play ");
list.add(" Bourgeois ");
list.add(" guitars ");
list.add(" and ");
list.add(" Huber ");
list.add(" banjos ");
System.out.println(StringBuilderTest.appendItemsToStirngBuffer(list));
} public static String appendItemsToStirngBuffer(List list) {
StringBuffer b = new StringBuffer();
for (Iterator i = list.iterator(); i.hasNext();) {
b.append(i.next()).append(" ");
}
return b.toString();
} public static void addToStringBuffer() {
List list = new ArrayList();
list.add(" I ");
list.add(" play ");
list.add(" Bourgeois ");
list.add(" guitars ");
list.add(" and ");
list.add(" Huber ");
list.add(" banjos ");
System.out.println(StringBuilderTest.appendItemsToStirngBuffer(list));
} public static void main(String[] args) {
stringTest();
stringBufferTest();
stringBuilderTest();
addToStringBuffer();
addToStringBuilder();
}
}

6、在<configuration>中,配置实现了拦截器的插件。Configuration就像是Mybatis的总管,Mybatis的所有配置信息都存放在这里,此外,它还提供了设置这些配置信息的方法。Configuration可以从配置文件里获取属性值,也可以通过程序直接设置。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> <!-- The content of element type "configuration" must match
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,
environments?,databaseIdProvider?,mappers?)".
Mybaits配置文件校验,节点位置有要求。 --> <!-- properties里配置的属性将被存放在Configuration的variables变量里,供Mybatis使用。 -->
<properties resource="jdbc.properties"></properties> <!-- 别名是为Java类型命名一个短的名字。它只用在XML配置文件里,用来减少类完全限定名的多余部分。 -->
<typeAliases>
<typeAlias alias="Company" type="com.dyl.entity.Company"/>
<typeAlias alias="Dep" type="com.dyl.entity.Dep"/>
<typeAlias alias="Duty" type="com.dyl.entity.Duty"/>
<typeAlias alias="Staff" type="com.dyl.entity.Staff"/>
</typeAliases> <!-- 自定义拦截模式,配置插件PagePlugin。属性dialect指示数据库类型,目前只支持mysql和oracle两种数据库。
属性pageSqlId指示拦截的规则,以正则方式匹配。元字符.点,匹配除“\n”之外的任何单个字符。元字符*,匹配前面的子表达式零次或多次(大于等于0次)。 -->
<plugins>
<plugin interceptor="com.dyl.util.PagePlugin">
<property name="dialect" value="oracle"/>
<property name="pageSqlId" value=".*ListPage.*"/>
</plugin>
</plugins> <!-- environments里可以配置多个environment,每个environment对应一个数据库环境。 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.pwd}" />
</dataSource>
</environment>
</environments> <!-- Mappers用于告诉Mybatis去哪里寻找sql映射文件。 -->
<mappers>
<mapper resource="com/dyl/entity/xml/Company.xml" /> </mappers> </configuration>

7、Company类和数据库表company一一对应。在Company的映射文件Company.xml中增加分页查询测试。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.dyl.dao.ICompanyDao"> <select id="selectCompanyById" parameterType="BigDecimal" resultType="Company">
select * from company where companyid = #{id}
</select> <!-- 为了返回list 类型而定义的returnMap -->
<resultMap type="Company" id="resultListCompany">
<id column="companyId" property="companyId" />
<result column="companyName" property="companyName" />
<result column="address" property="address" />
<result column="telephone" property="telephone" />
<result column="leader" property="leader" />
<result column="mobilePhone" property="mobilePhone" />
<result column="remark" property="remark" />
</resultMap> <!-- 返回list 的select 语句,注意 resultMap 的值是指向前面定义好的 -->
<select id="selectCompanys" parameterType="string" resultMap="resultListCompany">
select * from company where companyName like #{companyName} order by companyid
</select> <!--执行增加操作的SQL语句。id和parameterType分别与ICompanyDao接口中的addCompany方法的名字和参数类型一致。以#{name}的形式引用Company参数
的name属性,MyBatis将使用反射读取Company参数的此属性。#{name}中name大小写敏感。引用其他的gender等属性与此一致。useGeneratedKeys设置
为"true",表明要MyBatis获取由数据库自动生成的主键;keyProperty="companyid"指定把获取到的主键值注入到Company的companyid属性 -->
<insert id="addCompany" parameterType="Company" useGeneratedKeys="true" keyProperty="companyid">
insert into company(companyName,address,telephone,leader,mobilePhone,remark) values
(#{companyName},#{address},#{telephone},#{leader},#{mobilePhone},#{remark})
</insert> <update id="updateCompany" parameterType="Company">
update company set companyName=#{companyName},address=#{address},telephone=#{telephone},
leader=#{leader},mobilePhone=#{mobilePhone},remark=#{remark} where companyId=#{companyId}
</update> <delete id="deleteCompany" parameterType="BigDecimal">
delete from company where companyid=#{id}
</delete> <!-- 分页查询测试 -->
<select id="selectCompanyListPage" resultMap="resultListCompany">
select * from company
</select> </mapper>

8、在Company.xml的<mapper namespace="com.dyl.dao.ICompanyDao">dao接口中增加分页查询测试。映射文件Company.xml和接口ICompanyDao,三个一致,接口名字一致,方法名字一致,方法参数类型一致。

 package com.dyl.dao;

 import java.math.BigDecimal;
import java.util.List; import com.dyl.entity.Company;
import com.dyl.util.PageInfo; /**
* dao接口
* @author dyl
* @date 2014-7-13
*/
public interface ICompanyDao {
/**
* 根据分公司id查找分公司
*
* @param id
* @return
*/
public Company selectCompanyById(BigDecimal id); /**
* 查找分公司
*
* @param companyName
* @return
*/
public List<Company> selectCompanys(String companyName); /**
* 增加分公司
*
* @param company
*/
public void addCompany(Company company); /**
* 修改分公司
*
* @param company
*/
public void updateCompany(Company company); /**
* 删除分公司
*
* @param id
*/
public void deleteCompany(BigDecimal id); /**
* 分页查询测试
*
* @param page
* @return
*/
public List<Company> selectCompanyListPage(PageInfo page); /**
* 分页查找
* @param page 条件
* @return
*/
public List<?> findByPage(PageInfo page); /**
* 分页查找的总记录
* @param page 条件
* @return
*/
public int findByCount(PageInfo page); /**
* 修改公司信息
* @param company
* @return
* @throws Exception
*/
public Integer update(Company company) throws Exception;
}

9、测试。在测试前,我们运用Classloader 类加载器美化一下项目工程结构,在Java Resources在新建config,与src并行,把可以用Java类加载的配置文件移动到config。测试类中,有增加公司测试,可以看到Company类的属性,companyId是oracle主键自增长的(前面章节完成)。

 package com.dyl.test;

 import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.List; import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.dyl.dao.ICompanyDao;
import com.dyl.entity.Company;
import com.dyl.util.PageInfo;
/**
* 测试类
* @author dyl
* @date 2014-7-12
*/
public class CompanyXmlTest {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
// private static InputStream reader; /**
* Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中。
*
* (1)Class.getResourceAsStream(String path) : path不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取。
* 其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
*
* (2)Class.getClassLoader.getResourceAsStream(String path):默认则是从ClassPath根下获取,path不能以’/'开头,
* 最终是由ClassLoader获取资源。
*
* (3)Resources 类加载资源。
* 对于简单的只读文本数据,加载为Reader。
* 对于简单的只读二进制或文本数据,加载为 Stream。
* 对于可读写的二进制或文本文件,加载为 File。
* 对于只读的配置属性文件,加载为 Properties。
* 对于只读的通用资源,加载为 URL。
* 按以上的顺序,Resources类加载资源的方法如下:
* Reader getResourceAsReader(String resource);
* Stream getResourceAsStream(String resource);
* File getResourceAsFile(String resource);
* Properties getResourceAsProperties(String resource);
* Url getResourceAsUrl(String resource);
* 默认则是从ClassPath根下获取,resource不能以’/'开头,最终是由ClassLoader获取资源。
*/ static {
try {
reader = Resources.getResourceAsReader("sqlMap-config.xml");
// reader = Resources.class.getResourceAsStream("/sqlMap-config.xml");
// reader = Resources.class.getClassLoader().getResourceAsStream("sqlMap-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
} public static SqlSessionFactory getSession() {
return sqlSessionFactory;
} /**
* 查找分公司
* @param companyName
*/
public void getCompanyList(String companyName){
SqlSession session = sqlSessionFactory.openSession();
try {
ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
List<Company> companys = companyDao.selectCompanys(companyName);
for(Company company:companys){
System.out.println(company.getCompanyId().toString()+","+company.getCompanyName()+","
+company.getAddress());
}
} finally {
session.close();
}
} /**
* 测试增加,增加后,必须提交事务,否则不会写入到数据库。
*/
public void addCompany(){
Company company=new Company();
company.setCompanyName("妖灵科技");
company.setAddress("四川成都");
company.setTelephone("028-88888888");
company.setLeader("妖灵");
company.setMobilePhone("18888888888");
company.setRemark("HO");
SqlSession session = sqlSessionFactory.openSession();
try {
ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
companyDao.addCompany(company);
session.commit();
} finally {
session.close();
}
}
/**
* 先得到公司,然后修改,提交。
*/
public void updateCompany(){
SqlSession session = sqlSessionFactory.openSession();
try {
ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
Company company=companyDao.selectCompanyById(new BigDecimal(2));
company.setAddress("北京");
companyDao.updateCompany(company);
session.commit();
} finally {
session.close();
}
} /**
* 删除数据,删除一定要commit。
* @param id
*/
public void deleteCompany(BigDecimal id){
SqlSession session = sqlSessionFactory.openSession();
try {
ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
companyDao.deleteCompany(id);
session.commit();
} finally {
session.close();
}
} public static void main(String[] args) {
SqlSession session = sqlSessionFactory.openSession();
try {
ICompanyDao companyDao=session.getMapper(ICompanyDao.class); // // 根据分公司id查找分公司
// Company company=companyDao.selectCompanyById(new BigDecimal(5));
// System.out.println(company.getCompanyName());
// System.out.println(company.getAddress());
// System.out.println(); // // 查找分公司
// CompanyXmlTest companyTest=new CompanyXmlTest();
// companyTest.getCompanyList("%");
// System.out.println(); // // 增加分公司
// Company company=new Company();
// company.setCompanyName("海口分公司");
// company.setAddress("海南海口");
// company.setTelephone("0898-88888888");
// company.setLeader("碧波");
// company.setMobilePhone("18888888888");
// company.setRemark("东方夏威夷");
// companyDao.addCompany(company);
// session.commit();
// System.out.println("当前增加的公司名称为:"+company.getCompanyName()); // // 修改分公司
// Company company=companyDao.selectCompanyById(new BigDecimal(4));
// company.setAddress("天津");
// company.setCompanyName("天津分公司");
// company.setLeader("闻其");
// company.setMobilePhone("18888888888");
// company.setRemark("首都门户");
// company.setTelephone("022-88888888");
// companyDao.updateCompany(company);
// session.commit();
// System.out.println("修改成功"); // // 删除分公司
// companyDao.deleteCompany(new BigDecimal(5));
// session.commit();
// System.out.println("删除成功"); // 分页查询测试
PageInfo page=new PageInfo();
page.setShowCount(5);// 每一页显示多少
//page.setCurrentResult(5);// 当前显示到的id
page.setCurrentPage(2);// 当前页数
List<Company>companys=companyDao.selectCompanyListPage(page);
for(Company company:companys){
System.out.println(company.getCompanyId().toString()+","+company.getCompanyName()+","
+company.getAddress());
} } finally {
session.close();
}
}
}

10、项目工程图。

接下来,(1)我们需要根据实际开发需求选择使用struts2或者springmvc来实现控制层,选择前需要大概了解一下她们的差异,她们各自擅长什么。(2)确定框架组合后,碰到一些常用功能就封装成jar包,方便复用。(3)适当暂停,整理整理,测试测试,备份备份,休息休息,思考思考。很晕很晕。一定要勤于备份,一定要备份。OK,我们下次见。

个人知识管理系统Version1.0开发记录(10)的更多相关文章

  1. 个人知识管理系统Version1.0开发记录(09)

    MyBatis初试 MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.   运用Mybatis框 ...

  2. 个人知识管理系统Version1.0开发记录(01)

    架 构 描 述 01.数据层,数据源,有形资源入库,无形资源整理,对外搜索引擎,对内平台搜索,数据类型(文字.图片.声音.视频.组合),数据时空优化,数据安全方案(数据进站关卡,数据出战关卡),数据并 ...

  3. 个人知识管理系统Version1.0开发记录(02)

    第 一 步 做 什 么 我们该如何入手呢?先来看看目前常用的三个方法. 1.从事物产生的源头出发,层层推进,步步验证,最后开花结果.这种方法经常用于科研项目,或者三期以后的工程,国家政府项目用的较多. ...

  4. 个人知识管理系统Version1.0开发记录(12)

    最近碰到个问题,在五个工作日内阅读一个百万行左右代码量的新项目集合,如何解决呢? 第一个工作日,环境观察.待在那个项目组,看项目成员们在做些什么事情,开发,测试,聊天,或多或少可以收集到一些项目相关的 ...

  5. 个人知识管理系统Version1.0开发记录(07)

    模 块 复 用 原本还要测试一会的,突然出现一连串诡异的问题,比如,编译少加载个类啊,输入地址少个字母啊,改几行代码一改就是半小时啊.这是在提醒我们大脑疲倦了,所以果断小结,下次继续.这一次简单完成了 ...

  6. 个人知识管理系统Version1.0开发记录(11)

    (1)匹配单个属性的关键字:(2)匹配单个对象的关键字:(3)匹配对象集合的关键字:(4)基于事件驱动的:(5)实时搜索,参考win7的搜索功能. 1.备份,java代码,数据库数据. 2.oracl ...

  7. 个人知识管理系统Version1.0开发记录(05)

    demo controller我们从前面的实现过程可以得出三种普遍使用的信息处理方式:1.操作数据库,对数据进行增删改查,比如运用sqldevloper查看数据信息.2.运用计算机程序语言,对数据进行 ...

  8. 个人知识管理系统Version1.0开发记录(08)

    切入点 前面,我们已经搭建好了web端的一种基本结构,需要进一步定位的主要问题有三点: 1.界面的选择和确定,用extjs做的初步样式,进一步改动为jqueryUI/html,再进一步改变为HTML5 ...

  9. 个人知识管理系统Version1.0开发记录(06)

    demo view 夜已深,我们先简单演示一下,完成一个小段落了.涉及工具及技术知识:图形处理软件photoshop cs6,js类库ext. 思路如下: 1.下载ps6,有破解版本的,dll文件覆盖 ...

随机推荐

  1. Struts2的ActionContext

    web资源:HttpServletRequest,HttpSession,ServletContext等原生Servlet API Action中如何访问web资源: 1.和Servlet API解耦 ...

  2. 【译】Using Objects to Organize Your Code

    耗了一个晚上吐血翻译不过也学到了不少...<使用对象来组织你的代码>,翻译中发现原作者在原文中有部分代码有误或不全,本文已修改和添加~ 丽贝卡·墨菲原文链接:http://rmurphey ...

  3. angry_birds_again_and_again(2014年山东省第五届ACM大学生程序设计竞赛A题)

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2877 题目描述 The problems ca ...

  4. leetcode 数据库题解

    184. Department Highest Salary 题意: The Employee table holds all employees. Every employee has an Id, ...

  5. python学习之【16】网络编程

    主题 客户端/服务器架构 套接字:通信终点 套接字地址 面向连接与无连接套接字 Python中的网络编程 SOCKET模块 套接字对象方法 TCP/IP客户端和服务器 UDP/IP客户端和服务器 So ...

  6. Core Java 3

    p270~p272 1.创建异常类. 步骤:1)定义一个派生于(继承)Exception或者其子类的类. 2)添加两个构造方法:一个是默认构造器,另一个是带有详细描述信息的构造器. import ja ...

  7. A8逻辑篇1.点亮一个LED(S5PV210.A8)

    一.虚拟机安装好后,我们用Fedora 双击.vmx文件,将会在虚拟机中打开 相应的生成: 这些文件 二.进入虚拟机页面 等待启动 账号选择其他 用户名:root 密码:111111 设置页面大小: ...

  8. 再也不学AJAX了!(三)跨域获取资源 ③ - WebSocket & postMessage

    让我们先简单回顾一下之前谈到的内容,AJAX是一种无页面刷新的获取服务器资源的混合技术.而基于浏览器的"同源策略",不同"域"之间不可以发送AJAX请求.但是在 ...

  9. Git-远程仓库【转】

    本文转载自:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 远程仓库 到目前为止, ...

  10. node scripts/install.js 停顿解决办法

    参考:node-sass 安装卡在 node scripts/install.js 解决办法 在安装hexo的时候,运行: npm install hexo-cli -g 卡死在了 node scri ...