MyBatis 是一款优秀的持久层框架,它支持自定义 SQL。 MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的 XML 或注解来配置和映射。
省略了了创建数据库的连接,参数的拼接,数据结果的封装。
依赖作用:将许多需要在mabatis中进行配置的内容,在SpringBoot中就可以进行配置。
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
和customer bean中结构一致。

service中一个类表示一个业务,mapper中一个类表示一张表,每一个方法代表一个操作,如插入,查询等。
在mapper层创建类,com.hzy.spbt.demo.mapper.CustomerMapper,insertCustomer方法实现对表的插入操作。
@Mapper
public interface CustomerMapper {
@Insert("insert into customer(name,age) values (#{customer.name), #{customer.age}")
public void insertCustomer(@Param("customer") Customer customer);
}
com.hzy.spbt.demo.service.impl.CustomerServiceImpl。
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
CustomerMapper customerMapper;
@Override
public void saveCustomer(Customer customer) {
customerMapper.insertCustomer(customer);
System.out.println("service : saveCustomer:" + customer);
}
}
spring涉及到mabatis的判断可能不准确,上述代码可能会报错,可以按照下图弱化提示:

在application.properties中进行配置。
spring.datasource.url=jdbc:mysql://hadoop101:3306/user_profile_manage2022?characterEncoding=utf-8&useSSL=false
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
会发现JDBC驱动配置项报红,因为程序中并不包含JDBC驱动。
在pom文件中添加配置,引入驱动。
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
运行程序,通过浏览器插件发送请求,如下图

在控制台输出信息:service : saveCustomer:Customer(id=null, name=zhangsan, age=22)
在数据库中进行查看:

核心代码如下:
com.hzy.spbt.demo.service.impl.CustomerServiceImpl中的CustomerMapper方法增加:
List<Customer> customerList = customerMapper.selectCustomerList();
System.out.println(customerList);
com.hzy.spbt.demo.mapper.CustomerMapper中的CustomerMapper方法增加:
@Select("select * from customer")
public List<Customer> selectCustomerList();
执行程序,输出结果如下。

Mybatis-plus,简称 MP,是一个Mybatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,完全去SQL化,封装好了大量的CURD操作。 甚至把CRUD操作封装到了Service层,可以直接在controller调用现成的CRUD服务层,极度舒适省心。
可以省略mapper层的增删改查,也可以省略service层的增删改查。
局限:只支持简单的CRUD 操作。不支持多表操作(多表 join,union,子查询),不支持GroupBy 和各种函数(rank(),over())。
com.baomidou
mybatis-plus-boot-starter
3.4.1
在com.hzy.spbt.demo.mapper.CustomerMapper中继承BaseMapper。
com.hzy.spbt.demo.service.impl.CustomerServiceImpl中可以看到能够执行很多方法,其中黑色的为自定义方法。

可以尝试使用几种方法,这里以insert方法为例,增加以下代码:
customerMapper.insert(customer);
执行插入操作会发现,id不是按照原来的方式顺序递增,原因是使用主键的生成策略的问题。

需要对程序进行微调,在customer bean中增加:
@Data
public class Customer {
@TableId(value = "id",type = IdType.AUTO)
String id;
String name;
int age;
}
在实现类中编写:
// 无条件查询
List<Customer> customerList = customerMapper.selectList(new QueryWrapper<Customer>());
// 有条件查询
List<Customer> customerList1 = customerMapper.selectList(new QueryWrapper<Customer>().eq("name","zhangsan"));
System.out.println(customerList);
System.out.println(customerList1);
在service 接口、 serviceImpl 类、Mapper上增加 extends类,如下:
public interface CustomerService extends IService<Customer>
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper,Customer> implements CustomerService
直接在service接口 、mapper中直接使用已经实现的标准插删改查的方法。
这两个继承(扩展)已经帮助实现了很多标准操作,简化service层的增删改查操作,依赖mybatis-plus生成service层方法,这样标准的增删改查操作,service层都不用写,因此复杂的程序,或者是有一定的特点的程序,才会在service层中编写,如下,直接在Controller层中进行调用。
@PostMapping("/customer")
public String saveCustomer(@RequestBody Customer customer){
customerService.save(customer);
//customerService.saveCustomer(customer);
return "save user information: " + customer.toString();
}
省略了标准增删改查模块的业务类、实体类代码
com.baomidou
mybatis-plus-generator
3.4.1
org.apache.velocity
velocity-engine-core
2.0
实现功能:自动生成对某一张表的增删改查模块。
用代码生成器的工具类,其中修改数据库的路径用户名密码 ,表名,代码生成的路径。
public class CodeGenerator {
/**
*
* 读取控制台内容
*
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setFileOverride(true);
gc.setActiveRecord(false);// 不需要ActiveRecord特性的请改为false
gc.setEnableCache(false);// XML 二级缓存
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(false);// XML columList
gc.setOutputDir("E:\\develop\\Eclipse\\demo-spbt\\src\\main\\java"); //输出文件路径
gc.setAuthor("hzy");// 作者
gc.setOpen(false);
gc.setSwagger2(false); //实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL);
dsc.setUrl("jdbc:mysql://hadoop101:3306/user_profile_manage2022");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName(scanner("模块名"));
pc.setParent("com.hzy.userprofile");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setMapper("mapper");
pc.setEntity("bean");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
// strategy.setSuperEntityColumns("id");
strategy.setInclude(new String[] { "customer" }); // 需要生成的表
strategy.setControllerMappingHyphenStyle(true);
//strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.execute();
}
}
运行结果:

目前在application.properties中已经定义了一个JDBC数据库的配置,在实际情况中,一个模块可能连接多个数据库,这时如果没有任何工具,只依靠原生的mybatis,是一件很麻烦的事情。
想要连接多数据源,在java中需要引入支持多数据源的插件
com.baomidou
dynamic-datasource-spring-boot-starter
3.3.2
spring.datasource.dynamic.datasource.mysql2022.url=jdbc:mysql://hadoop101:3306/user_profile_manager2022?characterEncoding=utf-8&useSSL=false
spring.datasource.dynamic.datasource.mysql2022.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dynamic.datasource.mysql2022.username=root
spring.datasource.dynamic.datasource.mysql2022.password=123456
spring.datasource.dynamic.datasource.mysql2021.url=jdbc:mysql://hadoop101:3306/user_profile_manager2021?characterEncoding=utf-8&useSSL=false
spring.datasource.dynamic.datasource.mysql2021.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dynamic.datasource.mysql2021.username=root
spring.datasource.dynamic.datasource.mysql2021.password=123456
这样就实现了多数据源的连接。
使用时在对应的mapper层的接口上添加注解@DS(“配置中名称”),或者在特定的语句上添加注解,如下:
@Mapper
@DS("mysql2022")
public interface CustomerMapper extends BaseMapper<Customer> {
@Insert("insert into customer(name,age) values ( #{customer.name},#{customer.age} )")
public void insertCustomer2022(@Param("customer") Customer customer);
@DS("mysql2021")
@Insert("insert into customer(name,age) values ( #{customer.name},#{customer.age} )")
public void insertCustomer2021(@Param("customer") Customer customer);
@Select("select * from customer")
public List<Customer> selectCustomerList();
}
用户分群操作需要连接多数据源,MySQL数据库和ClickHouse数据库,这两个数据库都支持jdbc。
用户分群需要实现的几个功能如下图:

创建分群需要完成的任务如下图:

user_profile_manager_2022库中存在user_group表,表结构如下。
create table `user_group` (
`id` bigint ,
`user_group_name` varchar ,
`condition_json_str` varchar ,
`condition_comment` varchar ,
`user_group_num` bigint ,
`update_type` varchar ,
`user_group_comment` varchar ,
`update_time` datetime ,
`create_time` datetime
);
以下操作移步到user_profile_manager_2022项目中进行。
一般与数据库表一对一对应。
@Data
public class UserGroup implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO) //声明主键 主键默认的生成方式 Auto= 数据库的auto_increment
private Long id;
private String userGroupName;
private String conditionJsonStr;
// 一张表对应多个筛选条件,一对多在数据库中的存储方式有两种
// 1 存储两张表,一张主表,一张明细表
// 2 将多个筛选条件存储到数据库表中的一个字段中
// 原始数据为List结构,数据库中无法存储,所以先拿List结构接收,对应下面的tagConditions
// 转化为String后,存储到数据库中,对应上文的conditionJsonStr
// tagConditions和conditionJsonStr没有对应关系,需要使用@TableField
// 表明bean中虽然有这个字段,但并不是向数据库中存储的
// tagConditions作用,为了在程序中方便接收和计算
@TableField(exist = false) //声明 数据表中实际不存在该字段
private List<TagCondition> tagConditions;
private String conditionComment;
private Long userGroupNum;
private String updateType;
private String userGroupComment;
private Date updateTime;
private Date createTime;
@TableField(exist = false)
// 为了方便测试,在页面中可以随意填写业务日期
// 实际生产环境应该取当前时间的前一日
private String busiDate;
// 将不易看懂的JSON串转换为易于理解的中文串
public String conditionJsonToComment(){
StringBuilder comment=new StringBuilder();
for (TagCondition tagCondition : tagConditions) {
comment.append(tagCondition.tagName+ " "+tagCondition.operatorName+" "+ StringUtils.join(tagCondition.getTagValues(),",")+" ;\n");
}
return comment.toString();
}
}
目的:将页面传来的JSON串封装到UserGroupController类genUserGroup方法的参数userGroup中。
方法全部封装好,所以可以直接在Controller中实现,见(4)(5)。
通过userGroupService.saveOrUpdate(userGroup);就可以实现,但是在这之前还有一点其他工作需要实现,即在application.properties中使用动态数据源声明,在写入的时候需要声明默认使用哪个数据源,在Mapper和Service层中添加注解。
@PostMapping("/user-group")
public String genUserGroup(@RequestBody UserGroup userGroup){
userGroupService.saveOrUpdate(userGroup);
return "success";
}
完成后,可以在数据库中查看到相应数据,其中
condition_json_str :将条件转成字符串。
user_group_comment :将条件转成中文。
user_group_num :用户分群实际人数。
update_time create_time:更新时间,创建时间

下一步操作见(6),补充空白字段。
public interface UserGroupService extends IService {
}
虽然Service层中没有任何代码,但是继承了mybatis-plus中的 IService,其中封装了标准的增删改查,由UserGroupService的实现类UserGroupServiceImpl实现。
@Service
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl implements UserGroupService {
}
在ServiceImpl中实现,真正插入数据库的方法在UserGroupMapper中。
@DS("mysql")
@Mapper
public interface UserGroupMapper extends BaseMapper {
}
由BaseMapper中实现。
service层中不仅包含上述保存信息的一步操作,若在controller层直接调用方法,其他步骤也需要在此层中编写,所以在service层编写一个方法。
在UserGroupService接口中声明方法,在其实现类UserGroupServiceImpl中实现。
public void genUserGroup(UserGroup userGroup);
create_time和update_time存储在bean包的TagCondition类中,如下:
@Data
public class TagCondition {
String tagCode;
String tagName;
String operatorName;
String operator;
// 标签值,对应条件的包含,如包含男,女,未知
List<String> tagValues;
List<String> tagCodePath;
}
UserGroupServiceImpl中代码
@Override
public void genUserGroup(UserGroup userGroup){
// 1 写入mysql人群的基本定义
// (1)补充condition_json_str空白字段
List<TagCondition> tagConditions = userGroup.getTagConditions();
// 转成json串
String conditionJson = JSON.toJSONString(tagConditions);
// 再放回数据库中
userGroup.setConditionJsonStr(conditionJson);
// (2)补充condition_commen空白字段,将json串转成一个json说明
userGroup.setConditionComment(userGroup.conditionJsonToComment());
// (3)补充create_time空白字段
userGroup.setCreateTime(new Date());
// (4)user_group_num和update_time先不处理
// 调用父类方法
super.saveOrUpdate(userGroup);
// 2 写入ClickHouse人群包
// 3 人群包(包含所有uid)以应对高QPS访问
// redis(bitmap/set)
}
在controller层中进行调用:
userGroupService.genUserGroup(userGroup);
执行程序,添加分群信息,可以在数据库中查找到相应信息,如下图。
