整合通用Mapper到项目中
第一步:在jt-parent项目的pom.xml文件中依赖jar包
<!-- 通用Mapper -->
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>2.3.2</version>
</dependency>
第二步:加入Mapper
在下面的接口中扩展了批量删除功能,这是官方没有提供的。
SysMapper接口代码如下:
package com.jt.manage.mapper.base.mapper;
import java.util.List;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
public interface SysMapper<T> {
/**
* 根据主键ID批量删除
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int deleteByIDS(@Param("ids") Object[] key);
/**
* 根据实体类不为null的字段进行查询,条件全部使用=号and条件
*
* @param record
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
List<T> select(T record);
/**
* 根据实体类不为null的字段查询总数,条件全部使用=号and条件
*
* @param record
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int selectCount(T record);
/**
* 根据主键进行查询,必须保证结果唯一 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map
*
* @param key
* @return
*/
@SelectProvider(type = SysMapperProvider.class, method = "dynamicSQL")
T selectByPrimaryKey(Object key);
/**
* 插入一条数据 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写) 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*
* @param record
* @return
*/
@InsertProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int insert(T record);
/**
* 插入一条数据,只插入不为null的字段,不会影响有默认值的字段 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)
* 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长
*
* @param record
* @return
*/
@InsertProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int insertSelective(T record);
/**
* 根据实体类中字段不为null的条件进行删除,条件全部使用=号and条件
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int delete(T key);
/**
* 通过主键进行删除,这里最多只会删除一条数据 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map
*
* @param key
* @return
*/
@DeleteProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int deleteByPrimaryKey(Object key);
/**
* 根据主键进行更新,这里最多只会更新一条数据 参数为实体类
*
* @param record
* @return
*/
@UpdateProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int updateByPrimaryKey(T record);
/**
* 根据主键进行更新 只会更新不是null的数据
*
* @param record
* @return
*/
@UpdateProvider(type = SysMapperProvider.class, method = "dynamicSQL")
int updateByPrimaryKeySelective(T record);
}
SysMapperProvider.java拦截器代码如下:
package com.jt.manage.mapper.base.mapper;
import static org.apache.ibatis.jdbc.SqlBuilder.BEGIN;
import static org.apache.ibatis.jdbc.SqlBuilder.DELETE_FROM;
import static org.apache.ibatis.jdbc.SqlBuilder.SQL;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;
import com.github.abel533.mapper.MapperProvider;
import com.github.abel533.mapperhelper.EntityHelper;
import com.github.abel533.mapperhelper.MapperHelper;
public class SysMapperProvider extends MapperProvider {
public SysMapperProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public SqlNode deleteByIDS(MappedStatement ms) {
Class<?> entityClass = getSelectReturnType(ms);
Set<EntityHelper.EntityColumn> entityColumns = EntityHelper.getPKColumns(entityClass);
EntityHelper.EntityColumn column = null;
for (EntityHelper.EntityColumn entityColumn : entityColumns) {
column = entityColumn;
break;
}
List<SqlNode> sqlNodes = new ArrayList<SqlNode>();
// 开始拼sql
BEGIN();
// delete from table
DELETE_FROM(tableName(entityClass));
// 得到sql
String sql = SQL();
// 静态SQL部分
sqlNodes.add(new StaticTextSqlNode(sql + " WHERE " + column.getColumn() + " IN "));
// 构造foreach sql
SqlNode foreach = new ForEachSqlNode(ms.getConfiguration(), new StaticTextSqlNode("#{"
+ column.getProperty() + "}"), "ids", "index", column.getProperty(), "(", ")", ",");
sqlNodes.add(foreach);
return new MixedSqlNode(sqlNodes);
}
}
第三步:Mapper都继承SysMapper接口
package com.jt.manage.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.jt.manage.mapper.base.mapper.SysMapper;
import com.jt.manage.pojo.ItemCat;
public interface ItemCatMapper extends SysMapper<ItemCat>{
/**
* 根据ID查询商品分类数据,parentId = id(参数)
*
* @param id
* @return
*/
//List<ItemCat> queryListById(@Param("id") Long id);
}
第四步:在mybatis-config.xml中配置插件
<?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>
<settings>
<!-- 开启驼峰自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 二级缓存的总开关 -->
<setting name="cacheEnabled" value="false" />
</settings>
<plugins>
<!-- 分页插件:com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 方言 -->
<property name="dialect" value="mysql" />
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询,查询数据总条数 -->
<property name="rowBoundsWithCount" value="true" />
</plugin>
<!-- 通用Mapper插件 -->
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<!--主键自增回写方法,默认值MYSQL,详细说明请看文档 -->
<property name="IDENTITY" value="MYSQL" />
<!--通用Mapper接口,多个通用接口用逗号隔开 -->
<property name="mappers" value="com.jt.manage.mapper.base.mapper.SysMapper" />
</plugin>
</plugins>
</configuration>
第五步:pojo文件加上注解
package com.jt.manage.pojo;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "tb_item_cat")
public class ItemCat extends BasePojo{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// 主键自增长策略
private Long id;
// 父分类ID=0时,代表的是一级的分类
@Column(name = "parent_id")
private Long parentId;
private String name;
// 状态。可选值:1(正常),2(删除)
private Integer status;
// 排列序号,表示同级分类的展现次序,如数值相等则按名称次序排列。取值范围:大于零的整数
@Column(name = "sort_order")
private Integer sortOrder;
// 该分类是否为父分类,1为true,0为false
@Column(name = "is_parent")
private Boolean isParent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getSortOrder() {
return sortOrder;
}
public void setSortOrder(Integer sortOrder) {
this.sortOrder = sortOrder;
}
public Boolean getIsParent() {
return isParent;
}
public void setIsParent(Boolean isParent) {
this.isParent = isParent;
}
// 扩展get方法,满足EasyUI的tree格式
public String getText() {
return getName();
}
public String getState() {
return getIsParent() ? "closed" : "open";
}
}
第六步:Service方法也跟着修改
package com.jt.manage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jt.manage.mapper.ItemCatMapper;
import com.jt.manage.pojo.ItemCat;
@Service
public class ItemCatService {
@Autowired
private ItemCatMapper itemCatMapper;
public List<ItemCat> queryListById(Long id) {
//return this.itemCatMapper.queryListById(id);
ItemCat ic = new ItemCat();
ic.setParentId(id);
return this.itemCatMapper.select(ic);
}
}
第七步:测试,控制台日志:
2015-05-08 15:47:41,105 [http-bio-8082-exec-1] [com.jt.manage.mapper.ItemCatMapper.select]-[DEBUG] ==> Preparing: SELECT IS_PARENT ISPARENT,PARENT_ID PARENTID,STATUS,NAME,UPDATED,CREATED,SORT_ORDER SORTORDER,ID FROM tb_item_cat WHERE PARENT_ID = ?
2015-05-08 15:47:41,182 [http-bio-8082-exec-1] [com.jt.manage.mapper.ItemCatMapper.select]-[DEBUG] ==> Parameters: 0(Long)
2015-05-08 15:47:41,262 [http-bio-8082-exec-1] [com.jt.manage.mapper.ItemCatMapper.select]-[DEBUG] <== Total: 19
第八步:进一步抽取Service
BaseService.java
package com.jt.manage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.jt.manage.mapper.base.mapper.SysMapper;
public abstract class BaseService<T> {
@Autowired
private SysMapper<T> sysMapper;
//传统写法,Spring4以下版本
// public abstract SysMapper<T> getSysMapper();
/**
* 根据主键查询数据
*
* @param id
* @return
*/
public T queryById(Object id) {
return this.sysMapper.selectByPrimaryKey(id);
}
/**
* 根据条件查询,多条件之间是 and 关系
*
* @param t
* @return
*/
public List<T> queryListByWhere(T t) {
return this.sysMapper.select(t);
}
/**
* 根据条件查询单条数据
*
* @param t
* @return
*/
public T queryByWhere(T t) {
List<T> list = queryListByWhere(t);
if (list != null && !list.isEmpty()) {
return list.get(0);
}
return null;
}
/**
* 查询所有数据
*
* @return
*/
public List<T> queryAll() {
return this.sysMapper.select(null);
}
/**
* 新增数据,使用全部字段
*
* @param t
*/
public void save(T t) {
this.sysMapper.insert(t);
}
/**
* 新增数据,使用不为null的字段
*
* @param t
*/
public void saveSelective(T t) {
this.sysMapper.insertSelective(t);
}
/**
* 根据id删除
*
* @param id
* @return
*/
public Integer deleteById(Object id) {
return this.sysMapper.deleteByPrimaryKey(id);
}
/**
* 根据ids删除
*
* @param ids
* @return
*/
public Integer deleteByIds(Object[] ids) {
return this.sysMapper.deleteByIDS(ids);
}
/**
* 根据条件删除
*
* @param t
*/
public Integer deleteByWhere(T t) {
return this.sysMapper.delete(t);
}
/**
* 根据主键id更新数据
*
* @param t
*/
public Integer update(T t) {
return this.sysMapper.updateByPrimaryKey(t);
}
/**
* 根据主键id更新数据
*
* @param t
*/
public Integer updateSelective(T t) {
return this.sysMapper.updateByPrimaryKeySelective(t);
}
}
第九步:改造ItemCatService
package com.jt.manage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jt.manage.mapper.ItemCatMapper;
import com.jt.manage.pojo.ItemCat;
@Service
public class ItemCatService extends BaseService<ItemCat> {
@Autowired
private ItemCatMapper itemCatMapper;
public List<ItemCat> queryListById(Long id) {
//return this.itemCatMapper.queryListById(id);
ItemCat ic = new ItemCat();
ic.setParentId(id);
return this.itemCatMapper.select(ic);
}
}
问题:当父类需要一个对象时,子类如何注入?
例如:BaseService中需要SysMapper对象。
传统方式:
在父类中声明:
public abstract SysMapper<T> getSysMapper();
在子类中实现这个方法:
public class ItemCatService extends BaseService<ItemCat> {
@Autowired
private ItemCatMapper itemCatMapper;
public SysMapper<ItemCat> getSysMapper(){
return itemcatMapper;
}
……
注入方式:Spring4新加特性,通过泛型注入。
只需父类中声明:
public abstract class BaseService<T> {
@Autowired
private SysMapper<T> sysMapper;
……
常见问题
spring和springmvc容器间的关系
Spring会创建一个WebApplicationContext上下文,称为容器 ,保存在 ServletContext中,key是 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。可以使用Spring提供的工具类取出上下文对象:
WebApplicationContextUtils.getWebApplicationContext(ServletContext);
DispatcherServlet是一个Servlet,可以同时配置多个,每个DispatcherServlet有一个自己的上下文对象(SevletWebApplicationContext),称为子上下文(子容器),子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key 是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。 当一个Request对象产生时,会把这个子上下文对象(SevletWebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + ".CONTEXT"。
可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request); 说明:Spring 并没有限制我们,必须使用父子上下文。我们可以自己决定如何使用。
父上下文容器中保存数据源、服务层、DAO层、事务的Bean。子上下文容器中保存MVC相关的Action的Bean。事务控制在服务层。由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。
spring和springmvc都扫描了PageController
跟踪源码spring-beans中提供的DefaultListableBeanFactory中的392行
return StringUtils.toStringArray(this.beanDefinitionNames);
观察beanDefinitionNames可以看到在项目启动后,spring将Controller加载了。
、
Spring虽然扫描了Controller,但访问的请求无法拦截。sringmvc全部扫描,但service没有事务。
不想扫描多余的controller解决办法:
<context:component-scan
base-package="com.jt.common.service,
com.jt.manage.service" />
mybatis中的“假接口”
映射文件的内容:ItemCatMapper.xml
<mapper namespace="com.jt.manage.mapper.ItemCatMapper">
<select id="queryListById" parameterType="long" resultType="ItemCat">
SELECT * FROM tb_item_cat WHERE parent_id = #{id}
</select>
</mapper>
接口文件的内容:
package com.jt.manage.mapper;
public interface ItemCatMapper extends SysMapper<ItemCat>{
List<ItemCat> queryListById(@Param("id") Long id);
}
类中调用接口找映射文件中的sql,然后利用sqlSession提供方法来执行。
1) 获取接口,通过spring,spring和mybatis集成,配置接口扫描的目录。
2) 获取到这个接口的包路径和接口名称 等价于 映射文件中的命名空间,定位映射文件
3) 通过反射获取接口中的所有方法、返回值、参数,例如就可以获取到queryListById。这个名称就是映射文件中某个标签的id值。就找到要执行SQL语句。
4) 首先根据映射文件中找到的标签来判断,接口中返回值是一个List<Object>调用selectList,如果只是Object,调用selectOne。
源码分析:
如何执行一个查询
- SqlSession的实现类
- 初始化,通过sqlsession的getMapper方法获取Mapper接口的动态代理实例
初始化获得sqlSession的实例对象,并将所有Mapper保存在一个knownMappers的Map中。其key就是包名.类名=映射文件中的命名空间。value存放MapperProxy代理类。 - 最终查询时还是调用sqlSession的方法selectList