文章目录
一 数据层mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL。 MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的 XML 或注解来配置和映射。
省略了了创建数据库的连接,参数的拼接,数据结果的封装。
1 引入依赖
依赖作用:将许多需要在mabatis中进行配置的内容,在SpringBoot中就可以进行配置。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
2 创建表
和customer bean中结构一致。
3 创建Mapper类
service中一个类表示一个业务,mapper中一个类表示一张表,每一个方法代表一个操作,如插入,查询等。
在mapper层创建类,com.hzy.spbt.demo.mapper.CustomerMapper,insertCustomer方法实现对表的插入操作。
- @Param:标识参数。
- @Insert(“insert into customer(name,age) values (#{customer.name), #{customer.age}”):自动从customer对象中取出name和age属性。
- #{customer.name), #{customer.age}:不用加单引号,#会自动分别customer对象中每个属性的类型,是数字不用加单引号,是字符串会自动补上一个单引号。
- @Mapper:标识此类。
- 只写接口就可以,mabatis会根据注解和在application.properties中的配置,自动创建实现类。
- 此时这个接口可以直接让service.impl.CustomerServiceImpl实现类使用。
@Mapper
public interface CustomerMapper {
@Insert("insert into customer(name,age) values (#{customer.name), #{customer.age}")
public void insertCustomer(@Param("customer") Customer customer);
}
4 在实现类中调用
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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
5 运行程序
运行程序,通过浏览器插件发送请求,如下图
在控制台输出信息:service : saveCustomer:Customer(id=null, name=zhangsan, age=22)
在数据库中进行查看:
6 增加查找方法
核心代码如下:
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();
执行程序,输出结果如下。
7 总结
- 注解 : 目录下接口 xxxMapper 标识@Mapper
- Mapper接口的方法上 @Select @Insert @Update @Delete 实现sql方法
- 参数名前加@Param 声明变量 可以再SQL 以 #{ }方式引用 ${} (视情况补充单引、特殊符号的处理)、 引用 (完全字符替换)
- 在service使用mapper 需要用@Autowire 进行装配
- application.properties 要填写数据库地址,用户名密码 ,驱动
二 Mybatis-plus
Mybatis-plus,简称 MP,是一个Mybatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,完全去SQL化,封装好了大量的CURD操作。 甚至把CRUD操作封装到了Service层,可以直接在controller调用现成的CRUD服务层,极度舒适省心。
可以省略mapper层的增删改查,也可以省略service层的增删改查。
局限:只支持简单的CRUD 操作。不支持多表操作(多表 join,union,子查询),不支持GroupBy 和各种函数(rank(),over())。
1 引入依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
2 代码编写
在com.hzy.spbt.demo.mapper.CustomerMapper中继承BaseMapper<Customer>
。
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;
}
3 查询操作
在实现类中编写:
// 无条件查询
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);
4 简化操作
在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();
}
5 进一步简化 - 代码生成器
省略了标准增删改查模块的业务类、实体类代码
(1)引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
(2)源码
实现功能:自动生成对某一张表的增删改查模块。
用代码生成器的工具类,其中修改数据库的路径用户名密码 ,表名,代码生成的路径。
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
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();
}
}
运行结果:
6 总结
- 在service 接口、 serviceImpl 类、Mapper上 增加 extends 类。
- 直接在service接口 、mapper 中直接使用已经实现的标准插删改查的方法。
三 多数据源
目前在application.properties中已经定义了一个JDBC数据库的配置,在实际情况中,一个模块可能连接多个数据库,这时如果没有任何工具,只依靠原生的mybatis,是一件很麻烦的事情。
想要连接多数据源,在java中需要引入支持多数据源的插件
1 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
2 配置编写格式(application.properties)
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
这样就实现了多数据源的连接。
3 多数据源的使用
使用时在对应的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。
4 总结
- 用代码生成器的工具类,其中修改数据库的路径用户名密码 ,表名、代码生成的路径。
- 简化了多数据源操作。
- application.properties 配置多数据库源。
- 在类或者方法上通过@DS(“mysql2022”) 声明该方法或者类默认使用的数据源。
四 生成分群基本信息
用户分群需要实现的几个功能如下图:
1 创建分群点击操作
创建分群需要完成的任务如下图:
2 代码实现
(1)用户分群表结构
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项目中进行。
(2)UserGroup实体bean
一般与数据库表一对一对应。
@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();
}
}
(3)Controller层
目的:将页面传来的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),补充空白字段。
(4)Service层
public interface UserGroupService extends IService<UserGroup> {
}
虽然Service层中没有任何代码,但是继承了mybatis-plus中的 IService<UserGroup>
,其中封装了标准的增删改查,由UserGroupService的实现类UserGroupServiceImpl实现。
@Service
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {
}
在ServiceImpl<UserGroupMapper, UserGroup>
中实现,真正插入数据库的方法在UserGroupMapper
中。
(5)Mapper层
@DS("mysql")
@Mapper
public interface UserGroupMapper extends BaseMapper<UserGroup> {
}
由BaseMapper<UserGroup>
中实现。
(6)优化之补充空白字段
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);
执行程序,添加分群信息,可以在数据库中查找到相应信息,如下图。