一、Java实体类:
考虑到基本数据类型在Java类中都有默认值,会导致Mybatis在执行相关
操作的时候很难判断当前字段是否为null,所以在Mybatis环境下使用java
实体类的时候尽量不要使用基本数据类型,都使用对应的包装类型。
二、加入Maven依赖:
<!-- 通用Mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0-bate3</version>
</dependency>
三、修改spring配置文件:
将
<!-- 扫描dao接口 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.company.dao"/>
</bean>
改为
<!-- 扫描dao接口 -->
<bean id="mapperScanner" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.company.dao"/>
</bean>
默认是采用mysql数据库,其他数据库需要配置
四、dao文件:
import tk.mybatis.mapper.common.Mapper;
/**
* 具体操作数据库的Mapper接口,需要继承通用Mapper提供的核心接口 Mapper<T>
* 泛型类型为操作的数据库表对应的实体类
*/
public interface EmployeeDao extends Mapper<Employee> {
}
五、常用注解:
1.@Table注解
作用:当表名和实体类名不一致时,建立实体类和数据库表之间的对应关系
默认规则:实体类类名首字母小写作为表名。Employee类 ---> employee表
用法:在@Table注解的name属性中指定目标数据库表的表名
@Table(name="tabple_emp")
public class Employee{
private Integer empId;
private String empName;
private Double empSalary;
}
2.@Column注解
作用:建立实体类字段和数据库表字段之间的对应关系
默认规则:
实体类字段名:驼峰式命名,例如 empId
数据库表字段:使用"_"区分各个单词命名,例如 emp_id
用法:在@Column注解的name属性中指定目标数据库表的字段名
@Column(name="emp_salary_apple")
private Double empSalary; // 数据库表中字段名为 emp_salary_apple
3.@Id注解
通用Mapper在执行 xxxByPrimaryKey(key)方法时,有两种情况:
情况1:没有使用@Id注解指定主键字段
会报错,它会将实体类中所有的字段集合起来,作为联合主键
情况2:使用@Id注解明确标记和数据库中主键字段对应的实体类属性
4.@Transient注解
一般情况下,实体类中的字段和数据库表中字段是一一对应的,但是也有很多情况下会在实体类中
增加一些额外的属性,这种情况下就需要使用@Transient注解来告诉通用Mapper这不是表中字段。
5.@GeneratedValue
作用:让通用Mapper在执行insert操作之后将数据库自动生成的主键值回写到实体类对象中。
自增主键用法:
Employee employee= new Employee(null,"cat",6666.66,null);
employeeService.insert(employee); // dao调用insert
//一般情况下返回null
//如果empId为自增的主键,并且在实体类的 empId 属性上添加了 @Id 和 @GeneratedValue(strategy=GeneratedType.IDENTITY)
Integer empId = employee.getEmpId();
序列主键用法:
//如果是Oracle
@Id
@GeneratedValue(
strategy=GeneratedType.IDENTITY,
generator="select SEQ_ID.nextval from dual")
private Integer empId;
应用场景:购物车结账后
增加商品销量
减少商品库存
生成订单数据-->封装到Order对象中-->保存Order对象-->数据库自动生成主键值-->回写到实体类Order对象中
生成一系列订单详情数据-->List<OrderItem>-->在每一个OrderItem中设置Order对象的主键值作为外键-->批量保存List<OrderItem>
六、常用方法:
1.T selectOne(T var1)
通用Mapper替我们自动生成的SQL语句
方法中传入一个实体类,根据实体类对象参数,查询数据库中一条数据
Employee employeeQueryCondiction = new Employee(null,"bob",5560.11,null);
Employee employeeQueryResult = employeeService.selectOne(employeeQueryCondiction); //dao调用selectOne
实体类封装查询条件生成WHERE子句的规则:
·使用非空的值生成WHERE子句
·在条件表达式中都是使用 "=" 进行比较
要求必须返回一个实体类结果,如果有多个,会抛出异常
2.List<T> selectAll()
查询所有,返回List集合,不需要参数
3.List<T> select(T var1)
通过实体类对象,封装查询条件,可以查询多个,返回List集合
4.int selectCount(T var1)
查询个数
5.T selectByPrimaryKey(Object var1)
根据主键查询,需要根据一个主键,使用@Id注解明确标记和数据库中主键字段对应的实体类属性。
否则:会报错,它会将实体类中所有的字段集合起来,作为联合主键
6.boolean existsWithPrimaryKey(Object var1)
7.int insert(T var1)
插入数据操作
Employee employee= new Employee(null,"cat",6666.66,null);
employeeService.insert(employee); // dao调用insert
//一般情况下返回null
//如果empId为自增的主键,并且在实体类的 empId 属性上添加了 @GeneratedValue(strategy=GeneratedType.IDENTITY)
Integer empId = employee.getEmpId();
8.xxxSelective(T var1)
非主键字段如果为null值,则不会加入到SQL语句中
例如:int insertSelective(T var1)
适合表中字段挺多,但是插入数据很少是场景
可以省略插入数据是为null的字段,使得sql语句比较简洁可以省略插入数据是为null的字段,使得sql语句比较简洁
9.int updateByPrimaryKey(T var1)
10.int updateByPrimaryKeySelective(T var1)
在改数据库中的数据,并不是所有的数据都需要更改,这是需要采用这个方法。
11.int delete(T var1);
在条件表达式中都是使用 "=" 进行比较
注意:如果var1=null; 会将所有的数据都删除掉,需要先进行判断。
12.int deleteByPrimaryKey(Object var1);
七、QBC查询
query by criteria
criteria:是criterion的复数形式。意思是:规则、标准。在SQL语句中相当于查询条件。
QBC查询是将查询条件通过Java对象进行模块化封装。
例子:
目标: WHERE (emp_salary>? AND emp_age<?) OR (emp_salary<? AND emp_age>?)
//1.创建Example对象
Example example = new Example(Employee.class);
/*
example可以设置:
//1.设置查询结果排序信息
example.orderBy("empSalary").asc().orderBy("empAge").desc();
//2.设置查询结果“去重”
example.setDistinct(true);
//3.设置查询结果select字段
example.selectProperties("empSalary","empAge");
*/
//2.通过Example对象创建Criteria对象
Criteria criteria01 = example.createCriteria();
Criteria criteria02 = example.createCriteria();
//3.在两个Criteria对象中分别设置查询条件
criteria01.addGreaterThan("empSalary",3000).andLessThan("empAge",25);
criteria02.addLessThan("empSalary",5000).andGreaterThan("empAge",30);
//4.使用OR关键字组装两个Criteria对象
example.or(criteria02);
//5.执行查询
List<Employee> empList = employeeService.getEmpListByExample(example);
/*
在service中使用dao接口调用
dao.selectByExample(example);
*/
八、类型处理器:TypeHandler
1.简单类型和复杂类型
基本数据类型:byte,short,int,long,float,double,char,boolean
引用数据类型:类、接口、数组、枚举....
简单类型:只要一个值的类型
复杂数据类型:有多个值的类型
通用Mapper默认情况下会忽略复杂类型,对复杂类型不进行类到表的映射。
2.自定义类型转换器
一般情况下表中的数据字段会和实体类属性对应,但也有不对应的情况:
1)一对多、多对多的关系
这时需要建立关联表
2)有时候业务需求,需要一些特殊类型的数据
可以使用通用Mapper的类型转换器
Address对象 <-----> TypeHandler <-----> 字符串
3.实例1
1)新建一个类,继承通用Mapper的BaseTypeHandler,重写方法,实现自定义Address类型转换:
public class AddressTypeHandler extends BaseTypeHandler<Address>{
@Override
public void setNotNullParameter(PreparedStatement ps, int i, Address address,
JdbcType jdbcType) throws Exception{
//1.对数据进行验证
if(address == null){
return ;
}
//2.从address对象中取出具体数据
String province = address.getProvince();
String city = address.getCity();
String street = address.getStreet();
//3.拼装成一个字符串
// 自定义规则:各个值之间使用","分开
StringBuilder builder = new StringBuilder();
builder
.append(province).append(",")
.append(city).append(",")
.append(street);
String parameterValue = builder.toString();
//4.设置参数
ps.serString(i,parameterValue);
}
@Override
public Address getNullableResult(ResultSet rs,String columnName)throws SQLException{
//1.根据字段名从rs对象中获取字段值
String columnValue = rs.getString(columnName);
//2.验证columnName是否有效
if(columnValue == null || columnValue.length == 0 || !columnValue.contains(",")){
reruen ;
}
//3.根据","columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province,city,street);
return address;
}
@Override
public Address getNullableResult(ResultSet rs,int columnIndex)throws SQLException{
//1.根据索引从rs对象中获取字段值
String columnValue = rs.getString(columnIndex);
//2.验证columnName是否有效
if(columnValue == null || columnValue.length == 0 || !columnValue.contains(",")){
reruen ;
}
//3.根据","columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province,city,street);
return address;
}
@Override
public Address getNullableResult(CallableStatement cs, int columnIndex)throws Example{
//1.根据索引从cs对象中获取字段值
String columnValue = cs.getString(columnIndex);
//2.验证columnName是否有效
if(columnValue == null || columnValue.length == 0 || !columnValue.contains(",")){
reruen ;
}
//3.根据","columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province,city,street);
return address;
}
}
2)注册自定义类型转换器
方法一:字段级别:@ColumnType注解
public class user{
private Integer userId;
private String userName;
@ColumnType(typeHandler=AddressTypeHandler.class)
private Address address;
private SeasonEnum season;
....
}
方法二:全局级别:在Mybatis配置文件中配置 typeHandlers
//第一步:在实体类中该字段上加入@Column,作用是让通用Mapper不忽略这个类型
public class user{
private Integer userId;
private String userName;
@Column
private Address address;
private SeasonEnum season;
....
}
//第二步:在mybatis.xml文件中配置
<typeHandlers>
<!--handler属性:指定自定义类型转换器全限定名称-->
<!--javaType属性:指定需要自定义类型转换器转换的实体类的全限定名称-->
<typeHandler
handler="com.company.handlers.AddressTypeHandler"
javaType="com.company.domain.Address"/>
</typeHandlers>
4.实例2 枚举类型
办法一:让通用Mapper把枚举类型作为简单类型处理
增加一个通用Mapper的配置项,在ApplicationContrxt.xml配置通用Mapper的bean中
<!-- 扫描dao接口 -->
<bean id="mapperScanner" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.company.dao"/>
<property name="properties">
<value>
enumAsSimpleType=true
</value>
</property>
</bean>
办法二:为枚举类型配置对应的类型处理器
枚举对象 <-----> TypeHandler <-----> 字符串
枚举类型转换器:
1)mybatis内置两种处理器:
org.apache.ibatis.type.EnumTypeHandler<E> //在数据库中存储枚举值本身
org.apache.ibatis.type.EnumOrdinalTypeHandler<E> //在数据库中仅仅存储枚举值索引
内置枚举类型转换器的注册:
由于泛型的存在,不能使用@ColumnType注解注册mybatis内置的枚举类型转换器
可以在mybatis.xml文件中配置,但是注意不要加泛型
<typeHandlers>
<!--handler属性:指定自定义类型转换器全限定名称-->
<!--javaType属性:指定需要自定义类型转换器转换的实体类的全限定名称-->
<typeHandler
handler="org.apache.ibatis.type.EnumTypeHandler"
javaType="com.company.domain.SeasonEnum"/>
</typeHandlers>
2)自定义类型转换器