Mybatis和Hibernate是项目中最常用的两个持久层框架,Hibernate封装的比较完整,可移植性高,而Mybatis仅仅是一个半自动的ORM框架,但是Myabtis更轻量化,灵活性更好,效率更高,可个性化定制,SQL优化相对简单,这也是很多公司为什么选择使用Mybatis,而不是Hibernate的原因,Hibernate已经逐渐被JPA替代,隐退至底层,而不是直接被使用。
使用Mybatis小伙伴的在嘲笑Hibernate臃肿的同时又羡慕人家面向对象操作的便捷,于是,基于Mybatis二次封装的框架应运而生,如TkMybatis、MyBatis-Plus,本文将主要介绍MyBatis-Plus的使用,MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生,提供了很多面向对象的操作方法。至于其特性和框架结构这里就不赘述了,可以详看官网(官网地址)。
划重点:
a.由于MyBatis-Plus更新频率比较快,而且部分版本变化较大,本文会根据官方的更新及时调整文章内容,确保内容的正确性。
b.引入 MyBatis-Plus 之后就不要再次引入 MyBatis 以及 MyBatis-Spring相关包,以避免因版本差异导致问题。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
lombok依赖是为简化实体引入,非必须。
版本说明:使用SpringBoot 2.1.5.RELEAS、JDK1.8、MySQL数据库8.0.16
先建一张表,这里建一张物流公司表
-- ----------------------------
-- Table structure for logistics
-- ----------------------------
DROP TABLE IF EXISTS `logistics`;
CREATE TABLE `logistics` (
`id` mediumint(6) NOT NULL AUTO_INCREMENT,
`ecode` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '物流公司编码',
`ename` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '物流公司名称',
`short_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '物流公司简称',
`logistics_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电子面单物流类型',
`mailno_regular` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '运单号正则表达式',
`account_no` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '帐号',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
`pay_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '付款方式',
`express_type` int(11) NULL DEFAULT NULL COMMENT '快件产品类型',
`monthly_card_no` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '月结卡号',
`weight_type` int(11) NULL DEFAULT NULL COMMENT '称重方式',
`send_city_code` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发货城市编号',
`url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '直连电子面单URL',
`cainiao_url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜鸟模板URL',
`ad_org_id` bigint(20) NULL DEFAULT NULL COMMENT '所属组织',
`isactive` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'Y' COMMENT '是否可用',
`ad_client_id` bigint(20) NULL DEFAULT NULL COMMENT '所属公司',
`ownerid` bigint(20) NULL DEFAULT NULL COMMENT '创建人id',
`ownerename` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人姓名',
`ownername` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人用户名',
`creationdate` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`modifierid` bigint(20) NULL DEFAULT NULL COMMENT '修改人id',
`modifierename` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人姓名',
`modifiername` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人用户名',
`modifieddate` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `id`(`id`) USING BTREE COMMENT 'id'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '物流公司档案' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
server.port=9006
###########数据库连接信息###############
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
既然是映射框架,肯定需要将表与实体类、表字段与实体属性一一对应。
@TableName(value = "logistics")
@Data
public class Logistics {
@TableField(fill = FieldFill.INSERT)
@TableId(value = "id", type = IdType.INPUT)
private Long id;
@TableField(value = "ecode")
private String ecode;
private String ename;
private String shortName;
private String logisticsType;
private String mailnoRegular;
private String accountNo;
private String password;
private String payType;
private Integer expressType;
private String monthlyCardNo;
private Integer weightType;
private String sendCityCode;
private String url;
private String cainiaoUrl;
private Long adOrgId;
private String isactive;
private Long adClientId;
private Long ownerid;
private String ownerename;
private String ownername;
private Date creationdate;
private Long modifierid;
private String modifierename;
private String modifiername;
private Date modifieddate;
}
实体类注解说明:
(1)@TableName 表名注解,用来确立表和实体的关系
(2)@TableId 主键注解,这个不用多说,肯定是置于ID属性上了。
(3)@TableField 字段注解(非主键),确立实体属性与表字段的关系。
如果和数据库表字段一致或者表字段使用了下划线,实体属性将下划线替换为大写,也就是驼峰,如is_delete对应isDelete,就可以省略该注解。因为我建的表字段都满足,因此全部省略。
属性可以比字段少,如果属性没有对应的表字段,应该使用注解的exist属性标识(是否为数据库表字段)。
字段为null的更不更新跟字段的策略有关,默认不更新。改变策略strategy=FieldStrategy.IGNORED,就会忽略null值的判断,null就会进行更新。
字段策略源码:
/**
* 字段策略枚举类
*
* @author hubin
* @since 2016-09-09
*/
public enum FieldStrategy {
/**
* 忽略判断
*/
IGNORED,
/**
* 非NULL判断
*/
NOT_NULL,
/**
* 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
*/
NOT_EMPTY,
/**
* 默认的,一般只用于注解里
* 1. 在全局里代表 NOT_NULL
* 2. 在注解里代表 跟随全局
*/
DEFAULT
}
(4)@Version 乐观锁注解、标记 @Verison 在字段上
(5)@EnumValue 通用枚举类注解(注解在枚举字段上),这个会在后面详解。
(6)@TableLogic 表字段逻辑处理注解(逻辑删除)
(7)@SqlParser 租户注解
(8)@KeySequence 序列主键策略 oracle
映射关系已经确立好了,只需要创建XXMapper 接口, 并继承 BaseMapper 接口。
@Mapper
public interface LogisticsMapper extends BaseMapper<Logistics> {
}
至此,我们就可以使用MyBatis-Plus封装的各种方法了。
注意:如果觉得在每个mapper接口上加一个@Mapper比较麻烦,可以选择去掉所有接口的注解,而在启动类上加上@MapperScan(“com.mybatisPlus.mapper”)进行统一扫描,value是mapper接口所在路径。
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisPlusApplicationTests {
@Autowired
private LogisticsMapper LogisticsMapper;
@Test
public void test025() {
Logistics logistics = new Logistics();
logistics.setEcode("123");
logistics.setEname("顺丰快递");
logistics.setShortName("SF");
int insert = LogisticsMapper.insert(logistics);
}
}
MybatisPlus会自动把当前插入对象在数据库中的id写回到该实体中,通过get方法得到id。
注入mapper接口,这里IDEA可能会报无法注入,这个不用管,属于误报。
根据id修改
Logistics logistics = new Logistics();
logistics.setId(10L);
logistics.setEcode("123");
logistics.setEname("天天快递");
logistics.setShortName("TT");
int i = LogisticsMapper.updateById(logistics);
根据条件修改
将ename是“天天快递”的全部更新
int update = LogisticsMapper.update(logistics, new UpdateWrapper().eq("ename", "天天快递"));
构造器说明:
xxxWrapper是条件构造器,可以构造不同的组合条件,下面是构造器的继承图,可以根据不同的场景选择不同的构造器:
构造器条件 | 说明 |
---|---|
eq | 等于 = |
ne | 不等于 <> |
gt | 大于 > |
ge | 大于等于 >= |
lt | 小于 < |
le | 小于等于 <= |
between | BETWEEN 值1 AND 值2 |
notBetween | NOT BETWEEN 值1 AND 值2 |
like | LIKE ‘%值%’ |
notLike | NOT LIKE ‘%值%’ |
likeLeft | LIKE ‘%值’ |
likeRight | LIKE ‘值%’ |
isNull | 字段 IS NULL |
isNotNull | 字段 IS NOT NULL |
in / inSql | 字段 IN (value.get(0), value.get(1), …),例: in(“age”,{1,2,3})—>age in (1,2,3) |
notIn / notInSql | 字段NOT IN (value.get(0), value.get(1), …) |
groupBy | 分组:GROUP BY 字段, … |
orderBy / orderByAsc | 排序:ORDER BY 字段, … ASC |
orderByDesc | 排序:ORDER BY 字段, … DESC |
having | HAVING ( sql语句 ) |
or | 注意事项:主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接) |
and | AND 嵌套 |
last | 无视优化规则直接拼接到 sql 的最后 |
exists | 拼接 EXISTS ( sql语句 ) |
notExists | 拼接 NOT EXISTS ( sql语句 ) |
nested | 正常嵌套 不带 AND 或者 OR 例: nested(i -> i.eq(“code”, “123”).ne(“status”, “1”))—>(code= ‘123’ and status <> ‘1’) |
根据id删除
int delete = LogisticsMapper.deleteById(10);
根据id集合批量删除
List list = new ArrayList<>();
list.add(1L);
list.add(2L);
int i = LogisticsMapper.deleteBatchIds(list);
根据条件删除—map构造条件
Map map = new HashMap<>();
map.put("ecode", 123);
map.put("ename", "顺丰");
int delete = LogisticsMapper.deleteByMap(map);
注意:map构造的条件之间是and的关系,并且key是表中的字段名,而不是属性名。
根据条件删除—Wrapper构造器(不常用)
int delete = LogisticsMapper.delete(new Wrapper<Logistics>() {
@Override
public Logistics getEntity() {
Logistics logistics = new Logistics();
logistics.setEcode("123");
logistics.setEname("中通");
return logistics;
}
@Override
public MergeSegments getExpression() {
//合并 SQL 片段
return null;
}
@Override
public String getCustomSqlSegment() {
return null;
}
@Override
public String getSqlSegment() {
return null;
}
});
根据id查询,返回一个实体;
T selectById(Serializable id)
根据id集批量查询,返回实体集;
List selectBatchIds(idList)
根据map构造条件查询,注意是and关系;
List selectByMap(columnMap);
根据Wrapper条件构造器查询,返回实体,要确保结果唯一,否则会抛出异常;
T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);
根据Wrapper条件构造器查询记录数;
Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper);
根据Wrapper条件构造器查询,返回实体集;
List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
根据Wrapper条件构造器查询,返回字段映射对象 Map 集合
List
根据Wrapper条件构造器查询,返回第一个字段的值集合,如表第一个字段是id,那就返回id集合;
List
根据Wrapper条件构造器分页查询,返回分好页的全部记录,返回实体分页对象;
IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
根据Wrapper条件构造器分页查询,返回分好页的全部记录,返回字段映射对象 Map 分页对象;
IPage
上面查询都是将所有字段返回,有时候业务上并不需要这么多的字段,而且查询所有字段比较耗时,MP也提供了查询指定字段的方法,如下:
List ocBOrders1 = LogisticsMapper.selectList(new LambdaQueryWrapper().select(Logistics::getId, Logistics::getEcode).eq(Logistics::getId, 1));
List ocBOrders2 = LogisticsMapper.selectList(Wrappers.query().select("id", "ecode").eq("id", 1));
List ocBOrders3 = LogisticsMapper.selectList(new QueryWrapper().select("id", "ecode").eq("id", 1));
三种方式都可以,select里指定需要查询的字段,返回的依然是实体集,但是因为没有查询其他字段,其他属性都是null。
注意:条件构造器可以为null,代表没有条件,查询所有,但是按照id集批量查询或者修改的时候传参是不能为空的。
另外:分页查询需要配合分页插件。如果不配置,其默认采用的分页为RowBounds的分页即逻辑分页,也就是先把数据记录全部查询出来,然在再根据offset和limit截断记录返回(性能差,甚至会把服务器搞崩),而通过分页插件的配置即可达到物理分页效果。
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class);
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
使用mapper能满足大部分人的需求,但是MP还提供了Service CRUD 接口,
只需要让你的service类继承MP提供的ServiceImpl
@Service
public class LogisticsService extends ServiceImpl<LogisticsMapper, Logistics> {
public void queryById(){
Logistics logistics = this.getById(1);
//或者super.getById(1);
}
}
为了和Mapper CRUD 接口提供的方法区分开来,加了方法前缀,getxxx查询、removexxx 删除、listxxx 查询集合、pagexxx 分页、savexxx新增(提供了批量新增)。这里就不一一演示了,大家可以试试。
如果mapper和service提供的方法还是无法满足你,或者你还是喜欢用SQL,so easy,因为MP是包含Mybatis的,你可以直接将他当作Mybatis使用,在mapper中写SQL。
@Mapper
public interface LogisticsMapper extends BaseMapper<Logistics> {
@Select("SELECT ECODE FROM LOGISTICS WHERE ID=#{id}")
String getEcodeById(Long id);
}
ActiveRecord(以下简称AR)模式在新版本中的官文没有提及,但是并没有移除,因为不需要注入XXMapper,AR操作是通过对象本身调用相关方法,new的对象之后直接调用方法操作即可。
1、实体类
实体类需要继承Model,重写方法,return当前类的主键
@Override
protected Serializable pkVal() {
return id;
}
2、mapper接口
@Mapper
public interface LogisticsMapper extends BaseMapper<Logistics> {
}
虽然AR模式用不到mapper接口,但是一定要定义,否则使用AR时会报空指针异常。
3、使用
CRUD都不止一个方法,这里只演示一个。
(1)、新增
logistics.insert();
(2)、更新
logistics.updateById();
(3)、查询
logistics.selectById();
(4)、删除
boolean b = logistics.deleteById();
返回的是布尔,true删除成功,false删除失败,删除数据库中不存在的数据返回false
(5)、AR分页操作:
IPage selectPage = logistics.selectPage(new Page<>(1, 4), new QueryWrapper().like("ecode", "123"));
List list = selectPage.getRecords();
注意:如果需要看方法的底层sql,需要在配置文件中配置一下:
#控制台打印底层sql
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
很多业务下,前端显示的是逻辑值,但是数据库存的却是数值,如男在数据库对应1,女对应2,这里就需要进行字段的转换,不管是后端做还是前端做,都是一件很麻烦的事情,如果一两个还好,但是这种字段多的话代码繁琐不优雅,MP提供了解决方法,就是通用枚举。
(1)首先,将该类字段直接定义为枚举;
转化字段需要JSON序列化处理,只要是json传输,就需要进行JSON序列化处理,因为我们传给前端的就是json字符串。
@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private SexEnum sex;
这里使用的是Fastjson,如果是Jackson,则是在需要响应描述字段的get方法上添加@JsonValue注解即可。
(2)枚举类
定义对应枚举类
public enum SexEnum {
MALE(1, "男"), FEMALE(2, "女");
@EnumValue//用于标记数据库存的是那个字段的值
private final int code;
private final String descp;
SexEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
public int getCode() {
return code;
}
//重写toString方法,返回逻辑值
@Override
public String toString() {
return descp;
}
}
(3)application.propertis文件增加配置
#mybatis-Plus配置,枚举类所在包路径
mybatis-plus.typeEnumsPackage=com.mybatisPlus.model.enums
这样就搞定了。
MP的强大不止于此,它提供了很多附属功能,如分布式事务、多数据源切换、SQL性能监控(p6spy实现)、代码生成器等等。因为表字段和实体属性是一一对应的,如果表中字段几十上百,写一个实体类简直就是折麽人,MP提供的代码生成器不仅可以生成实体类,还可以生成mapper、xml、service、controller,有了这个神器再也不担心实体类生成了,如果字段名称改的比较多,直接重新生成就好了,不用再一个个找出来修改,大大的提高了效率。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖
官文中的示例好久没更新了,所以需要稍作调整,copy下面代码,修改相关信息,直接主类运行即可。
public class CodeGenerator {
public static void main(String[] args) {
//代码生成器
AutoGenerator mpg = new AutoGenerator();
mpg.setGlobalConfig(globalConfig());
mpg.setDataSource(dataSourceConfig());
mpg.setStrategy(strategyConfig());
mpg.setPackageInfo(packageConfig());
mpg.execute();
}
/**
* 全局配置
*/
private static GlobalConfig globalConfig() {
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(false) // 是否开启AR模式
.setAuthor("sunies") //生成的文件头@author
.setOutputDir("C:/Users/sunies/Desktop") //生成路径
.setFileOverride(true) //文件是否覆盖
.setIdType(IdType.AUTO) // 主键策略
.setServiceName("%sService") //文件名称方式,例如: %sAction 生成 UserAction, %s 为占位符默认情况下生成的Service接口的名字首字母都带有I
.setBaseResultMap(true) // 是否生成基本的sql中的ResultMap
.setBaseColumnList(true) // 是否生成基本的sql列
.setSwagger2(true)//实体属性 Swagger2 注解
.setOpen(false);//生成后是否自动打开文件夹
return config;
}
/**
* 数据库连接信息
*
* @return
*/
private static DataSourceConfig dataSourceConfig() {
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL);
dsc.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
return dsc;
}
/**
* 策略配置
*/
private static StrategyConfig strategyConfig() {
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true); // 是否大写命名
// 表前缀
//strategyConfig.setTablePrefix("");
strategyConfig.setNaming(NamingStrategy.underline_to_camel); // 从数据库表到文件的命名策略
//需要包含的表名,允许正则表达式(与exclude二选一配置)
strategyConfig.setInclude("logistics"); // 数据库表名,需要存在
//数据库表字段映射到实体的命名策略,原样输出no_change,下划线转驼峰命underline_to_camel;
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
//自定义继承的Entity类全称,带包名
//strategyConfig.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
//是否为lombok模型(默认 false)
strategyConfig.setEntityLombokModel(true);
//生成控制器
strategyConfig.setRestControllerStyle(true);
//实体是否生成 serialVersionUID
strategyConfig.setEntitySerialVersionUID(true);
// 自定义继承的Controller类全称,带包名
//strategyConfig.setSuperControllerClass("com.baomidou.ant.common.BaseController");
//自定义基础的Entity类,公共字段
//strategyConfig.setSuperEntityColumns("id");
//驼峰转连字符
//strategyConfig.setControllerMappingHyphenStyle(true);
//表前缀
//strategyConfig.setTablePrefix();
return strategyConfig;
}
/**
* 包名策略配置
*/
private static PackageConfig packageConfig() {
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.demo"); // 总包名
packageConfig.setEntity("model"); // 实体生成后所在包名
packageConfig.setController("controller");
packageConfig.setService("service");
packageConfig.setMapper("mapper");
packageConfig.setXml("mapper");
return packageConfig;
}
/**
* 自定义配置
*
* @return
*/
private static InjectionConfig injectionConfig() {
InjectionConfig injectionConfig = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return "C:/Users/sunies/Desktop" + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录");
return false;
}
});
*/
injectionConfig.setFileOutConfigList(focList);
return injectionConfig;
}
/**
* 配置模板
*
* @return
*/
private static TemplateConfig templateConfig() {
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
return templateConfig;
}
}