1、看包:
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!--tk mybatis-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!--PageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
2、看配置文件:
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username = root
spring.datasource.password = root
3、看实体注解:
@Data //lombok注解
@Table(name = "tb_student") //映射表注解
public class Student implements Serializable {
@Id
@Column(insertable = false)
//因为用的PGSQL,这里一定要写明查询序列语句,坑点较多
@GeneratedValue(strategy = GenerationType.IDENTITY,generator = "select nextval('student_id_seq'::regclass)")
private Integer id;
private String name; //如果不使用@Column或者下划线转换等,则需要和数据库字段保持一致
private String address;
}
4、看通用mapper
public interface MyMapper<T> extends Mapper<T>,InsertListMapper<T> {
}
这里有个坑...,只需要继承就行了,不需要写其他东西,记得别忘了保持泛型
5、看通用service,可选,通用mapper可以直接通过@Autowired直接装配使用了,不过最好定义一个通用service
public interface BaseService<T,TD> {
void save(T model);//持久化
void save(List<T> models);//批量持久化
void deleteById(TD id);//通过主鍵刪除
void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4” //这里有个坑
void update(T model);//更新
T findById(TD id);//通过ID查找
T findBy(String fieldName, Object value) throws TooManyResultsException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束
List<T> findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4” //这里有个坑
List<T> findByCondition(Condition condition);//根据条件查找
List<T> findAll();//获取所有
}
6、看Service实现
public class AbstractService<T,TD> implements BaseService<T,TD> {
@Autowired
private MyMapper<T> mapper;
private Class<T> clazz;
public AbstractService(){
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); //获取超类类型,带泛型
clazz = (Class<T>) pt.getActualTypeArguments()[0]; //获取泛型类型,就一个泛型,取数组第一个就行了
}
@Override
public void save(T model) {
mapper.insertSelective(model);
}
@Override
public void save(List<T> models) {
mapper.insertList(models);
}
@Override
public void deleteById(TD id) {
mapper.deleteByPrimaryKey(id);
}
@Override
public void deleteByIds(String ids) {
mapper.deleteByIds(ids);
}
@Override
public void update(T model) {
mapper.updateByPrimaryKeySelective(model);
}
@Override
public T findById(TD id) {
return mapper.selectByPrimaryKey(id);
}
@Override
public T findBy(String fieldName, Object value) throws TooManyResultsException {
T model = null;
try {
model = clazz.newInstance(); //实例化对象
Field field = clazz.getDeclaredField(fieldName); //获取属性
field.setAccessible(true); //暴力破解private
field.set(model, value); //属性赋值
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return mapper.selectOne(model); //查询
}
@Override
public List<T> findByIds(String ids) {
return mapper.selectByIds(ids);
}
@Override
public List<T> findByCondition(Condition condition) {
return mapper.selectByCondition(condition);
}
@Override
public List<T> findAll() {
return mapper.selectAll();
}
}
PS:如果是tk.mapper需要获取泛型的话,涉及到了动态代理,需要子类teacherMapper.getClass.getInterfaces()[0].getGenericInterfaces()[0]
//动态代理获取到的class不是teacherMapper,需要先获取接口
//getGenericInterfaces()[0]:获取直接父接口的第一个接口
7、看PageHelper的基本使用
PageHelper.startPage(1,1); //设置分页数据,该方法之后的第一个select方法会进行分页
PageInfo page = PageInfo.of(service.findAll()); //获得page数据
PS:PageHelper在面对连表查询的时候会出现总记录数错乱,例如A表中的属性包含了N个B表,解决方法是在xml中分表,使用<collection select="" proerty="" column="" ofType="">,select指明查询B表,property表示A表中的List<B>属性,ofType表示B表的javaBean,即List<B>中的泛型B,column表示A表要传到B表的数据,对应A表的数据库字段或者resultMap映射的column字段
8、添加一个bean,摒弃注解扫描mapper
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//自己的mapper位置
mapperScannerConfigurer.setBasePackage("com.fengwuJ.tkmybatis.mymapper");
Properties propertiesMapper = new Properties();
// 通用mapper位置,接口全路径
propertiesMapper.setProperty("mappers", "com.fengwuJ.tkmybatis.basemapper.MyMapper");
propertiesMapper.setProperty("notEmpty", "false");
//主键UUID回写方法执行顺序,默认AFTER,可选值为(BEFORE|AFTER)
propertiesMapper.setProperty("ORDER", "BEFORE");
mapperScannerConfigurer.setProperties(propertiesMapper);
return mapperScannerConfigurer;
}
ps:大写的坑点
插入
SqlServerMapper—>InsertMapperàSqlServerProvideràinsert:
INSERT INTO tb_teacher ( name,update_time,is_deleted ) VALUES ( ?,?,? )
直接跳过了id的拼接
@Options(
useGeneratedKeys = true
)
@InsertProvider(
type = SqlServerProvider.class,
method = "dynamicSQL"
)
int insert(T var1);
使用@Options(useGeneratedKeys = true) 回显主键
@Id:指定主键
@Column:指定数据库查询出来的记录字段(也可以是别名)与实体属性的映射关系,不配置则默认相同名称映射
InsertMappeàInsertMapperà BaseInsertProvideràinsert
- Id上需添加 @Column(insertable = false)
- 使用@Options(useGeneratedKeys = true) 回显主键
PS: insertable:默认为true,设置为false时不会拼接该字段
条件查询:
- Condition查询: 首先,声明一点,Condition也是可以创建Criteria使用java语言创建条件查询的;Condition condition = new Condition(Entity.class); condition.createCriteria().andCondition(“name=”,"呵呵").orCondition()....最后将condition传入selectByExample或者selectByCondition()方法
- Example查询:首先,也声明一点,Example也是可以通过创建Criteria使用andCondition进行sql拼接查询的;Example example = new Example(Student.class); Example.Criteria criteria = example.createCriteria(); criteria.andCondition("name like","%和%"); criteria.andEqualTo("age",10);criteria.andLike("name","%和%");条件设置完后,将example传入selectByExample或者selectByCondition()方法即可。
- 总结:其实这两个查询都是一样,本质上都是通过Criteria进行构建查询条件,继续跟踪源码发现,Example类下有一个List<Example.Criteria> creteria,大多数方法的操作,criteria的派生方法,都是经过一次次转换,通过逻辑判断是and/or、equal/like,最后变成Creteria(String Condition,Object value),被添加到example中的GeneratedCriteria
内部类的List<Example.Criterion> criteria中。 - 再说一下,selectByCondition()和selectByExample();condition是Example的子类,selectByExample通过ExampleProvider这个类进行动态解析MappedStatement数据,进行动态拼接sql;Condition底层就是直接调的ExampleProvider;但是这个MappedStatement和Condition以及Example有什么关系,我传入的查询条件与ms怎么映射上的还不得而知
复杂的条件查询(a/b/c) (d):
- 第一种方式:要实现以上的结构要么通过weekend.createCriteria,用ctriteria创建三个条件,再用Example创建一个criteria,最后用weekend.and连接,三个以上同理
- 第二种方式:使用Example创建两个criteria,一个创建三个条件,一个创建一个条件,然后随意选取一个criteria.and(Criteria),三个以上同理