# mybatis-plus的配置
mybatis-plus:
type-aliases-package: com.example.demo.entity # 配置包别名
mapper-locations: classpath:mappers/*.xml # 映射文件的位置
configuration:
map-underscore-to-camel-case: true # true自动开启驼峰规则映射
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mybatis-plus 日志
save
// 插入一条记录
int insert(T entity);
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(User::getAge, 18).set(User::getAge, 20);
@Override
@Transactional(rollbackFor = Exception.class)
public CommcauseElementVO save(@NonNull CommcauseElementVO commcauseElementVO, @NonNull UserVO currUserVO) {
if (Objects.nonNull(commcauseElementVO.getElementId())) {
this.commcauseElementMapper.update(commcauseElementVO, Wrappers.<CommcauseElement>lambdaUpdate()
.eq(CommcauseElement::getElementId, commcauseElementVO.getElementId()));
} else {
commcauseElementVO.setElementId(this.seqService.genLongSequence(CommcauseConstants.SEQ_NAME));
this.commcauseElementMapper.insert(commcauseElementVO);
}
return commcauseElementVO;
}
注意
MP的update方法,如果字段为null,则不会被更新,需要再用set更新为null
this.commcauseElementMapper.update(commcauseElementVO, Wrappers.<CommcauseElement>lambdaUpdate()
.set(Objects.isNull(commcauseElementVO.getDependElement()), CommcauseElement::getDependElement, null)
.eq(CommcauseElement::getElementId, commcauseElementVO.getElementId()));
只更新某个字段
return this.commcauseInfoMapper.update(null, Wrappers.<CommcauseInfo>lambdaUpdate()
.set(CommcauseInfo::getCauseStatus, CommCauseStatus.DELETED).eq(CommcauseInfo::getCauseId, causeId));
更新比较多的,先删除后增加
QueryWrapper<BannerItem> wrapper = new QueryWrapper<>();
wrapper.eq("banner_id", id);
List<BannerItem> bannerItems = bannerItemMapper.selectList(wrapper);
LambdaQueryWrapper<BannerItem> wrapper = new QueryWrapper<BannerItem>().lambda();
wrapper.eq(BannerItem::getBannerId, id);
List<BannerItem> bannerItems = bannerItemMapper.selectList(wrapper);
简化,链式
List<BannerItem> bannerItems = new LambdaQueryChainWrapper<>(bannerItemMapper)
.eq(BannerItem::getBannerId, id)
.list();
//
BannerItem bannerItem = new LambdaQueryChainWrapper<>(bannerItemMapper)
.eq(BannerItem::getId, id)
.one();
@Override
public IPage<CommcauseElementVO> listPage(@NonNull CommcauseElementListPageDTO commcauseElementListPageDTO, @NonNull Integer current, @NonNull Integer size) {
LambdaQueryWrapper<CommcauseElement> wrappers = Wrappers.<CommcauseElement>lambdaQuery()
.eq(StringUtils.isNotBlank(commcauseElementListPageDTO.getElementCode()),
CommcauseElement::getElementCode,
commcauseElementListPageDTO.getElementCode())
.eq(StringUtils.isNotBlank(commcauseElementListPageDTO.getElementName()),
CommcauseElement::getElementName,
commcauseElementListPageDTO.getElementName());
IPage<CommcauseElementVO> selectPage =
this.commcauseElementMapper.selectPage(new Page<>(current, size), wrappers);
return selectPage;
}
前面都是可以加条件的,条件为true时,才会执行
eq(boolean condition, R column, Object val)
eq
allEq 例:allEq({id:1,name:“老王”,age:null})->
id = 1 and name = ‘老王’ and age is null
ne
gt
ge
lt
le
between,notBetween
like,notLike,likeLeft(‘%王’),likeRight(‘王%’)
isNull,isNotNull
in,notIn,例:in(“id”, new Object[]{1, 2}),in(“id”, list)
inSql,notInSql
groupBy
orderByAsc,orderByDesc
orderBy
having 例:having(“sum(age) > {0}”, 11) -> having sum(age) > 11
or 例:eq(“id”, 1).or().eq(“id”, 2)
and
exists,notExists 例: exists("select id from table")
—>exists (select id from table)
func
nested
apply
last
func(Consumer<Children> consumer)
func(boolean condition, Consumer<Children> consumer)
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
nested(Consumer<Param> consumer)
nested(boolean condition, Consumer<Param> consumer)
正常嵌套 不带 AND 或者 OR
例: nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')
@Configuration
public class MPConfig {
/**
* 分页插件 MP 3.2之前
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
// MP 3.2之后
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
IPage属性
record getRecords 查询出来的记录
total getTotal总条数
size getSize页的大小
current getCurrent当前页
pages getPages总页数
searchCount isSerachCount默认是true,是否返回符合查询条件的条数
@Override
public IPage<CommcauseSchemeVO> listPage(@NonNull CommcauseSchemeListPageDTO listPageDTO, @NonNull Integer current, @NonNull Integer size) {
LambdaQueryWrapper<CommcauseScheme> wrappers = Wrappers.<CommcauseScheme>lambdaQuery()
.eq(StringUtils.isNotBlank(listPageDTO.getSchemeName()), CommcauseScheme::getSchemeName, listPageDTO.getSchemeName())
.eq(StringUtils.isNotBlank(listPageDTO.getSchemeStatus()), CommcauseScheme::getSchemeStatus, listPageDTO.getSchemeStatus())
.eq(Objects.nonNull(listPageDTO.getIsCanImport()), CommcauseScheme::getIsCanImport, listPageDTO.getIsCanImport());
IPage<CommcauseSchemeVO> selectPage = this.commcauseSchemeMapper.selectPage(new Page<>(current, size), wrappers);
// 方式一:方法引用
selectPage.getRecords().forEach(this::translate);
// 方式二:lambda表达式
/*
List records = selectPage.getRecords();
records.forEach(commcauseSchemeVO -> {
commcauseSchemeVO.setSchemeStatusName(this.translate(commcauseSchemeVO));
});
*/
return selectPage;
}
private void translate(CommcauseSchemeVO commcauseSchemeVO) {
commcauseSchemeVO.setSchemeStatusName(this.dictionaryItemBI.getItemNameByDictCodeAndItemValue(
CommcauseConstants.CommCauseDicCode.SCHEME_STATUS, commcauseSchemeVO.getSchemeStatus(), null));
// 方式二:
// return this.dictionaryItemBI.getItemNameByDictCodeAndItemValue(
// CommcauseConstants.CommCauseDicCode.SCHEME_STATUS, commcauseSchemeVO.getSchemeStatus(), null);
}
public IPage<CommcauseElementVO> listPage(@NonNull CommcauseElementListPageDTO commcauseElementListPageDTO,
@NonNull Integer current, @NonNull Integer size) {
LambdaQueryWrapper<CommcauseElement> wrappers = Wrappers.<CommcauseElement>lambdaQuery()
.eq(StringUtils.isNotBlank(commcauseElementListPageDTO.getElementCode()), CommcauseElement::getElementCode,
commcauseElementListPageDTO.getElementCode())
.eq(StringUtils.isNotBlank(commcauseElementListPageDTO.getElementName()), CommcauseElement::getElementName,
commcauseElementListPageDTO.getElementName());
IPage<CommcauseElementVO> selectPage =
this.commcauseElementMapper.selectPage(new Page<>(current, size), wrappers);
List<CommcauseElementVO> records = selectPage.getRecords();
int max = records.size();
for (int i = 0; i < max; i++) {
int finalI = i;
Optional.ofNullable(records.get(i).getDependElement()).ifPresent(dependElement ->
Optional.ofNullable(this.commcauseElementMapper.selectById(dependElement))
.ifPresent(commcauseElementVO1 -> records.get(finalI).setDependElementName(commcauseElementVO1.getElementName())));
}
return selectPage;
}
value = “id字段”, type = IdType.INPUT
IdType
值 | 描述 |
---|---|
AUTO | 数据库ID自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert前自行set主键值 |
ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认default方法) |
分布式全局唯一ID 长整型类型(please use ASSIGN_ID ) |
|
32位UUID字符串(please use ASSIGN_UUID ) |
|
分布式全局唯一ID 字符串类型(please use ASSIGN_ID ) |
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | “” | 数据库字段名 |
el | String | 否 | “” | 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | “” | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,参考(opens new window) |
update | String | 否 | “” | 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性) |
insertStrategy | Enum | N | DEFAULT | 举例:NOT_NULL: insert into table_a( |
updateStrategy | Enum | N | DEFAULT | 举例:IGNORED: update table_a set column=#{columnProperty} |
whereStrategy | Enum | N | DEFAULT | 举例:NOT_EMPTY: where |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC类型 (该默认值不代表会按照该值生效) |
typeHandler | Class extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | “” | 指定小数点后保留的位数 |
分布式系统唯一ID生成方案汇总
https://www.cnblogs.com/haoxinyue/p/5208136.html
3.2之后变为
AUTO 主键自增,开发者无需赋值
NONE(默认) 设置主键,通过雪花算法实现
INPUT 主键需要开发者手动赋值,如果没有手动赋值,则通过自增方式赋值
ASSIGN_ID 主键类型为Long、Integer、String,通过雪花算法自动赋值
ASSIGN_UUID 主键的数据类型为String,不包含下划线的UUID
主键生成策略必须使用INPUT
支持父类定义@KeySequence子类继承使用
支持主键类型指定(3.3.0开始自动识别主键类型)内置支持:
- DB2KeyGenerator
- H2KeyGenerator
- KingbaseKeyGenerator
- OracleKeyGenerator
- PostgreKeyGenerator
如果内置支持不满足你的需求,可实现IKeyGenerator接口来进行扩展
举个栗子
@KeySequence("SEQ_USER")
public class User {
@TableId(value = "id", type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
}
springboot
方式一:
@Configuration
public class MybatisPlusConfig {
/**
* sequence主键,需要配置一个主键生成器
* 配合实体类注解 {@link KeySequence} + {@link TableId} type=INPUT
* @return
*/
@Bean
public H2KeyGenerator h2KeyGenerator(){
return new H2KeyGenerator();
}
}
方式二:
@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator());
}
导包
<dependency>
<groupId>p6spygroupId>
<artifactId>p6spyartifactId>
<version>3.9.1version>
dependency>
application.yml
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:h2:mem:test
...
spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
该插件消耗性能,不建议在生产环境下使用
依赖
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.4.1version>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>2.0version>
dependency>
非默认模板引擎依赖
Freemarker
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
<version>2.3.23version>
dependency>
Beetl
<dependency>
<groupId>com.ibeetlgroupId>
<artifactId>beetlartifactId>
<version>latest-beetl-versionversion>
dependency>
注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。
AutoGenerator generator = new AutoGenerator();
// set freemarker engine
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// set beetl engine
generator.setTemplateEngine(new BeetlTemplateEngine());
启动测试类
@Test
void contextLoads() {
// 需要构建一个 代码自动生成器 对象
AutoGenerator mpg = new AutoGenerator();
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath+"/src/main/java");
gc.setAuthor("Chuang-2");
gc.setOpen(false); // 不打开资源管理器
gc.setFileOverride(false); // 是否覆盖
gc.setServiceName("%sService");// 去掉Service接口的首字母I
// gc.setIdType(IdType.AUTO);// 自增Id
gc.setIdType(IdType.ID_WORKER);// 雪花算法
gc.setDateType(DateType.TIME_PACK);// DateType.TIME_PACK LocalDateTime, DateType.SQL_PACK Timestamp,DateType.ONLY_DATE Date
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/edu?useSSL=false&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.demo");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = 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 projectPath + "/src/main/resources/mappers/" +
tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("tb_generator"); // 设置要映射的表名,多个表则逗号隔开
strategy.setTablePrefix(pc.getModuleName + "_"); // 去掉表前缀
strategy.setNaming(NamingStrategy.underline_to_camel);// 数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);// 据库表字段映射到实体的命名策略
strategy.setChainModel(true);// 开启链式编程
// strategy.setLogicDeleteFieldName("deleted");
// 自动填充配置
/*TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified",
FieldFill.INSERT_UPDATE);
ArrayList tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);*/
// strategy.setVersionFieldName("version");// 乐观锁
strategy.setRestControllerStyle(true);// RestController
// strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //执行
}
package com.baomidou.mybatisplus.core.toolkit
public interface Constants extends StringPool {
String MYBATIS_PLUS = "mybatis-plus";
String MD5 = "MD5";
String ENTITY = "et";
String ENTITY_DOT = "et.";
String WRAPPER = "ew";
String WRAPPER_DOT = "ew.";
String WRAPPER_ENTITY = "ew.entity";
String WRAPPER_SQLSEGMENT = "ew.sqlSegment";
String WRAPPER_EMPTYOFNORMAL = "ew.emptyOfNormal";
String WRAPPER_NONEMPTYOFNORMAL = "ew.nonEmptyOfNormal";
String WRAPPER_NONEMPTYOFENTITY = "ew.nonEmptyOfEntity";
String WRAPPER_EMPTYOFWHERE = "ew.emptyOfWhere";
String WRAPPER_NONEMPTYOFWHERE = "ew.nonEmptyOfWhere";
String WRAPPER_ENTITY_DOT = "ew.entity.";
String U_WRAPPER_SQL_SET = "ew.sqlSet";
String Q_WRAPPER_SQL_SELECT = "ew.sqlSelect";
String Q_WRAPPER_SQL_COMMENT = "ew.sqlComment";
String COLUMN_MAP = "cm";
String COLUMN_MAP_IS_EMPTY = "cm.isEmpty";
String COLLECTION = "coll";
String WHERE = "WHERE";
String MP_OPTLOCK_VERSION_ORIGINAL = "MP_OPTLOCK_VERSION_ORIGINAL";
String MP_OPTLOCK_VERSION_COLUMN = "MP_OPTLOCK_VERSION_COLUMN";
String MP_OPTLOCK_ET_ORIGINAL = "MP_OPTLOCK_ET_ORIGINAL";
String WRAPPER_PARAM = "MPGENVAL";
String WRAPPER_PARAM_FORMAT = "#{%s.paramNameValuePairs.%s}";
}
public interface StringPool {
String AMPERSAND = "&";
String AND = "and";
String AT = "@";
String ASTERISK = "*";
String STAR = "*";
String BACK_SLASH = "\\";
String COLON = ":";
String COMMA = ",";
String DASH = "-";
String DOLLAR = "$";
String DOT = ".";
String DOTDOT = "..";
String DOT_CLASS = ".class";
String DOT_JAVA = ".java";
String DOT_XML = ".xml";
String EMPTY = "";
String EQUALS = "=";
String FALSE = "false";
String SLASH = "/";
String HASH = "#";
String HAT = "^";
String LEFT_BRACE = "{";
String LEFT_BRACKET = "(";
String LEFT_CHEV = "<";
String DOT_NEWLINE = ",\n";
String NEWLINE = "\n";
String N = "n";
String NO = "no";
String NULL = "null";
String OFF = "off";
String ON = "on";
String PERCENT = "%";
String PIPE = "|";
String PLUS = "+";
String QUESTION_MARK = "?";
String EXCLAMATION_MARK = "!";
String QUOTE = "\"";
String RETURN = "\r";
String TAB = "\t";
String RIGHT_BRACE = "}";
String RIGHT_BRACKET = ")";
String RIGHT_CHEV = ">";
String SEMICOLON = ";";
String SINGLE_QUOTE = "'";
String BACKTICK = "`";
String SPACE = " ";
String TILDA = "~";
String LEFT_SQ_BRACKET = "[";
String RIGHT_SQ_BRACKET = "]";
String TRUE = "true";
String UNDERSCORE = "_";
String UTF_8 = "UTF-8";
String US_ASCII = "US-ASCII";
String ISO_8859_1 = "ISO-8859-1";
String Y = "y";
String YES = "yes";
String ONE = "1";
String ZERO = "0";
String DOLLAR_LEFT_BRACE = "${";
String HASH_LEFT_BRACE = "#{";
String CRLF = "\r\n";
String HTML_NBSP = " ";
String HTML_AMP = "&";
String HTML_QUOTE = """;
String HTML_LT = "<";
String HTML_GT = ">";
String[] EMPTY_ARRAY = new String[0];
byte[] BYTES_NEW_LINE = "\n".getBytes();
}