在使用mybatis标签的时候,需要指定类的全类名,比如 resultType=xx.xxx.xx
,但是我们可以为类指定别名,那么就可以直接使用别名,避免全类名冗余【不推荐使用】
别名的配置有两种方式,这里我们讲解简单的配置方式,步骤如下:
<typeAliases>
<packagename="cn.tedu.domain"/>
</typeAliases>
@Alias("author")
public class Author{
...
}
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
param1、param2....
#{param1}
@Param
指定key,那么就可以在sql语句中直接使用这个key即可,如下: @Select("select * from patient_info where ipt_num=#{iptNum} and status=#{status}")
PatientselectByIptNumAndInhos(@Param("iptNum")String iptNum, @Param("status")String status);
@Select("select * from patient_info where ipt_num=#{param1} and status=#{param2}")
PatientselectByIptNumAndInhos(String iptNum,String status);
@Select("select * from patient_info where ipt_num=#{iptNum} and status=#{status}")
PatientselectByIptAndInhosNumMap(Map ipts);
@Test
public void test3()throws IOException {
SqlSessionFactory sqlSessionFactory = XmlConfigInit.getSqlSessionFactory();
PatientMapper mapper = sqlSessionFactory.openSession().getMapper(PatientMapper.class);
Map<String,String> map=new HashMap<>();
map.put("iptNum","15627656");
map.put("status","1");
Patient patient = mapper.selectByIptAndInhosNumMap(map);
System.out.println(patient);
}
resultType
指定返回的POJO的全类名即可,或者指定别名 /**
* 使用@MapKey注解指定返回Map中的key,这里设定的是Patient中的id属性作为key
* @return
*/
@MapKey("id")
@Select("select * from patient_info")
Map<Integer,Patient>selectAllReturnMap();
@MapKey
这个注解指定
在mybatis中 collection
和 association
中都是可以使用分步查询
我们需要查询一个科室下的所有患者,那么实体类定义如下:
@Data
@ToString
public class Dept{
private Integer id;
private String name;
private List<Patient> patients;
}
@Data
@ToString
public class Patient{
private String userId;
private Integer id;
private String status;
}
<resultMapid="baseResultMap"type="cn.tedu.domain.Dept">
<idcolumn="id"property="id"/>
<resultcolumn="name"property="name"/>
<collectionproperty="patients"ofType="cn.tedu.domain.Patient">
<idcolumn="pid"property="id"/>
<resultcolumn="userId"property="userId"/>
<resultcolumn="pstatus"property="status"/>
</collection>
</resultMap>
//sql
SELECT
d.id AS id,
d.NAME AS NAME,
p.id AS pid,
p.user_id AS userId,
p.STATUS AS pstatus
FROM
dept_info d
LEFT JOIN patient_info p ON p.dept_id = d.id
WHERE
d.id =#{id,jdbcType=INTEGER}
但是我们也可以采用分布查询的方式,先查询出科室的信息,再根据科室的id查询出对应患者的信息,实现步骤如下:
<select id="selectById" resultMap="ByStepResultMap">
SELECT
*
FROM
dept_info
WHERE
id =#{id,jdbcType=INTEGER}
</select>
定义一个方法根据科室id查询患者信息
@Select("SELECT * from patient_info where dept_id=#{deptId}")
List<Patient>selectByDeptId(Integer deptId);
在resultMap中指定分步查询
<!--分步查询科室信息和患者信息-->
<resultMapid="ByStepResultMap"type="cn.tedu.domain.Dept">
<idcolumn="id"property="id"/>
<resultcolumn="name"property="name"/>
<!--ofType:指定级联属性的类型-->
<!--select:指定分步查询患者信息的方法,全类名+方法名-->
<!--column:指定查询科室获取的结果的哪一个字段作为查询患者方法的参数,可以指定多个
如果指定多个,那么需要将参数封装成map,比如column="{key1=column1,key2=column2}"
-->
<!--fetchType:在开启全局延迟加载的时候设置是否延迟加载,默认是延迟加载,可以设置为eager表示不延迟加载-->
<collectionproperty="patients"ofType="cn.tedu.domain.Patient"
select="cn.tedu.mapper.PatientMapper.selectByDeptId"
column="id">
</collection>
</resultMap>
<settings>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。-->
<settingname="lazyLoadingEnabled"value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。-->
<settingname="aggressiveLazyLoading"value="false"/>
</settings>
_parameter _databaseId
@Select("SELECT * from patient_info where dept_id=#{_parameter}")
List<Patient>selectByDeptId(Integer deptId);
_parameter
判断是否为空,如下: <iftest="_parameter!=null"> ..... </if>
_parameter.key1....
直接获取值即可,当然如果没有指定@Param注解,此时还可以使用 _parameter.param1,_parameter.param2...
直接获取对应的值 defaultExecutorType =BATCH
即可,不过这种方式将会导致所有的sql操作都会使用批量操作。 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
,此时当前的SqlSession执行的就是批量操作。 SqlSessionTemplate
,并且设置批量处理的属性即可,如下: /**
* 注入SqlSessionTemplate,替代SqlSessionFactory直接创建SqlSession,并且能够使用Spring的事务管理
* 如果需要使用批量处理,在构造方法中指定ExecutorType.BATCH即可,那么全部的操作的都会使用
* 【可以不配置,需要的时候使用】
*/
@Bean
public SqlSessionTemplate sqlSessionTemplate()throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject(),ExecutorType.BATCH);
return sqlSessionTemplate;
}
#{}
中的jdbcType属性的时候,mybaits框架会为我们设置相应的处理器,比如jdbcType=DATE,那么mybatis默认对应的处理器就是 DateOnlyTypeHandler
(只有年月日),如果我们设置了jdbcType=TIMESTAMP,那么mybatis对Date类型的类型处理器就是 DateTypeHandler
,关于类型处理器和jdbcType的对应关系,看 官方文档中typeHandler这一节
getResult
public interface TypeHandler<T>{
/**
* 为预编译语句设置参数的时候执行,实际就是调用setxxx方法设置参数
* @param ps PreparedStatement 对象
* @param i
* @param parameter 参数
* @param jdbcType #{}中自定义的jdbcType
* @throws SQLException
*/
void setParameter(PreparedStatement ps,int i, T parameter, JdbcType jdbcType)throws SQLException;
/**
* 根据列名获取指定的值
* @param rs ResultSet结果集
* @param columnName 列名
* @return
* @throws SQLException
*/
TgetResult(ResultSet rs, String columnName)throws SQLException;
/**
* 根据下标获取指定列的值
* @param rs ResultSet结果集
* @param columnIndex 下标
* @return
* @throws SQLException
*/
TgetResult(ResultSet rs,int columnIndex)throws SQLException;
/**
* 调用存储过程的获取返回值的时候
* @param cs
* @param columnIndex
* @return
* @throws SQLException
*/
TgetResult(CallableStatement cs,int columnIndex)throws SQLException;
}
TypeHandler BaseTypeHandler
List<Auth>
对象存入数据库的时候是以json字符串的形式,获取的是以List集合的形式,此时我们可以自定义一个TypeHandler,如下: /**
* 自定义类型转换器,将List<Auth>数据存入数据库的时候是以json字符串存入的,获取返回的结果的时候是List集合
* @MappedJdbcTypes(value = {JdbcType.VARCHAR}):指定了映射的jdbcType的类型是VARCHAR
* @MappedTypes(value = {Auth.class}):指定了映射的java类型是Auth
*/
@MappedJdbcTypes(value = {JdbcType.VARCHAR})
@MappedTypes(value = {Auth.class})
public class AuthTypeHandlerextends BaseTypeHandler{
/**
* 将参数转换为json数据存入数据库
*/
@Override
public void setNonNullParameter(PreparedStatement ps,int i, Object parameter, JdbcType jdbcType)throws SQLException {
String json = new Gson().toJson(parameter);
ps.setString(i,json);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)throws SQLException {
String string = rs.getString(columnName);
return new Gson().fromJson(string,new TypeToken<List<Auth>>(){}.getType());
}
@Override
public Object getNullableResult(ResultSet rs,int columnIndex)throws SQLException {
String string = rs.getString(columnIndex);
return new Gson().fromJson(string,new TypeToken<List<Auth>>(){}.getType());
}
@Override
public Object getNullableResult(CallableStatement cs,int columnIndex)throws SQLException {
return null;
}
}
<!--设置自动扫描包下的typeHandler-->
<typeHandlers>
<packagename="cn.tedu.typehandler"/>
</typeHandlers>
<insertid="insertAdmin"parameterType="cn.tedu.domain.Admin">
insert into t_admin(name,birthday,account,password,point,status,auths)
values (#{name,jdbcType=VARCHAR},#{birthday,jdbcType=DATE},
#{account,jdbcType=VARCHAR},#{password,jdbcType=VARCHAR},
#{point,jdbcType=DOUBLE},#{status,jdbcType=VARCHAR},#{auths,jdbcType=VARCHAR,typeHandler=cn.tedu.typehandler.AuthTypeHandler})
</insert>
List<AUth>
<resultMapid="BaseResultMap"type="cn.tedu.domain.Admin">
<idcolumn="id"property="id"/>
<resultcolumn="name"property="name"/>
<resultcolumn="auths"property="auths"javaType="cn.tedu.domain.Auth"jdbcType="VARCHAR"typeHandler="cn.tedu.typehandler.AuthTypeHandler"></result>
</resultMap>
EnumTypeHandler
,存入和取出都是存储的枚举的名称,也有一个 EnumOrdinalTypeHandler
是按照枚举的索引存储和查询的。 org.apache.ibatis.plugin.Interceptor
接口即可 public interface Interceptor{
/**
* 拦截器真正执行的方法,其中的invocation.proceed()是用来执行目标方法的,只有执行了这个proceed方法,目标方法才会执行,否则不执行
* @param invocation
* @return 返回目标方法执行的结果,return invocation.proceed();
* @throws Throwable
*/
Objectintercept(Invocation invocation)throws Throwable;
/**
* 四大对象生成代理对象的方法,在四大对象创建的时候,都会调用一个pluginAll方法返回一个代理对象
* 这个方法不需要做修改,默认就行了
* @param target 目标对象
* @return 返回的代理对象(层层包装,表示只有一层)
*/
default Object plugin(Object target){
return Plugin.wrap(target, this);
}
/**
* 在全局配置文件中<plugin>标签中设置properties属性,会封装在此方法的properties中
* @param properties
*/
default void setProperties(Properties properties){
// NOP
}
}
/**
* @Intercepts注解标记这是一个拦截器,其中可以指定多个@Signature
* @Signature:指定该拦截器拦截的是四大对象中的哪个方法
* type:拦截器的四大对象的类型
* method:拦截器的方法,方法名
* args:入参的类型
*/
@Intercepts(
{
@Signature(type = ParameterHandler.class,method ="setParameters",args = {PreparedStatement.class})
}
)
public class FirstPluginimplements Interceptor{
@Override
public Object intercept(Invocation invocation)throws Throwable {
System.out.println("拦截器执行:"+invocation.getTarget());
//目标对象
Object target = invocation.getTarget();
//获取目标对象中所有属性的值,因为ParameterHandler使用的是DefaultParameterHandler,因此里面的所有的属性都封装在其中
MetaObject metaObject = SystemMetaObject.forObject(target);
//使用xxx.xxx.xx的方式可以层层获取属性值,这里获取的是mappedStatement中的id值
String value = (String) metaObject.getValue("mappedStatement.id");
//如果是指定的查询方法
if ("cn.tedu.mapper.AdminMapper.selectById".equals(value)){
//设置参数的值是2,即是设置id=2,因为这里只有一个参数,可以这么设置,如果有多个需要需要循环
metaObject.setValue("parameterObject", 2);
}
//执行目标方法
return invocation.proceed();
}
//可以省略
@Override
public Object plugin(Object target){
return Plugin.wrap(target, this);
}
//可以省略
@Override
public void setProperties(Properties properties){
System.out.println(properties);
}
}
<!--配置插件,其中的property可以设置自己的属性,可以封装到setProperties中的properties中-->
<plugins>
<plugininterceptor="cn.tedu.plugin.FirstPlugin">
<propertyname="id"value="11"></property>
</plugin>
</plugins>
SystemMetaObject.forObject(target)
metaObject.getValue("mappedStatement.id")
metaObject.setValue(name, value)
如果有多个插件作用在同一个对象的同一个方法上,那么插件的执行顺序是怎样的?我们知道四大对象在创建的时候会调用拦截器中的plugin方法创建代理对象,这种代理实层层包装的,那么在后面的插件创建的代理是包裹在最外层的,因此肯定是先执行最外层的拦截器方法。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
@MapperScan SqlSessionFactoryBean SqlSessionTemplate
/**
* 配置类
* @MapperScan:扫描所有的Mapper接口
*/
@Configuration
@ComponentScan(basePackages = {"cn.tedu.ssm"})
@MapperScan(basePackages = {"cn.tedu.ssm.mapper"})
@EnableAspectJAutoProxy
@EnableAsync
@EnableTransactionManagement
public class MainConfig{
/**
* 注册数据源
*/
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(DataConfig.URL);
dataSource.setUsername(DataConfig.USER);
dataSource.setPassword(DataConfig.PWD);
dataSource.setDriverClassName(DataConfig.DRIVER_NAME);
return dataSource;
}
/**
* 配置SqlSessionFactoryBean,实际就是SqlSessionFactory
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactory()throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//设置数据源
sqlSessionFactoryBean.setDataSource(dataSource());
//配置扫描mapepr.xml文件
PathMatchingResourcePatternResolver classPathResource = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(classPathResource.getResources("classpath:mappers/*.xml"));
//设置全局配置文件的位置
sqlSessionFactoryBean.setConfigLocation(classPathResource.getResource("classpath:mybatis-config.xml"));
return sqlSessionFactoryBean;
}
/**
* 注入SqlSessionTemplate,替代SqlSessionFactory直接创建SqlSession,并且能够使用Spring的事务管理
* 如果需要使用批量处理,在构造方法中指定ExecutorType.BATCH即可,那么全部的操作的都会使用
* 【可以不配置,需要的时候使用】
*/
@Bean
public SqlSessionTemplate sqlSessionTemplate()throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
return sqlSessionTemplate;
}
/**
* 创建事务管理器
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSource());
}
}
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
/**
* 配置SqlSessionFactoryBean,实际就是SqlSessionFactory
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactory()throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//设置数据源
sqlSessionFactoryBean.setDataSource(dataSource());
//配置扫描mapepr.xml文件
PathMatchingResourcePatternResolver classPathResource = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(classPathResource.getResources("classpath:mappers/*.xml"));
//设置全局配置文件的位置
sqlSessionFactoryBean.setConfigLocation(classPathResource.getResource("classpath:mybatis-config.xml"));
//配置插件
PageInterceptor pageInterceptor = new PageInterceptor();
//可以配置PageHelper中的参数映射关系,这里使用默认的,不需配置
// pageInterceptor.setProperties();
sqlSessionFactoryBean.setPlugins(pageInterceptor);
return sqlSessionFactoryBean;
}
@Test
public void test3(){
SqlSessionTemplate sqlSessionTemplate = applicationContext.getBean(SqlSessionTemplate.class);
final PatientMapper mapper = sqlSessionTemplate.getMapper(PatientMapper.class);
PageInfo<Object> pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
@Override
public void doSelect(){
mapper.selectAllPatient();
}
});
}
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
/**
* 所有需要分页的请求要继承的类,其中提供了分页需要的参数
* 默认的映射关系是:pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
* 也可在设置拦截器的时候指定映射关系,具体看官方文档
* https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
*/
@Data
public class PageParam{
/**
* 当前第几页
*/
private Integer pageNum=1;
/**
* 每页查询的数量
*/
private Integer pageSize=10;
/**
* 是否进行count查询,默认是true,查询
* 如果设置为false,那么总数total将会为-1,不进行count查询
*/
@JsonIgnore
private Boolean countSql=true;
/**
* 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
*/
@JsonIgnore
private Boolean reasonable;
/**
* 默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
*/
@JsonIgnore
private Boolean pageSizeZero=true;
}
/**
* 分页需要实现的接口,在doExecute中只需调用查询全部数据的mapper即可
*/
@FunctionalInterface
public interface ExecutePageHelperextends ISelect{
/**
* 实现类应该覆盖的方法
*/
void doExecute();
@Override
default void doSelect(){
doExecute();
}
}
/**
* 执行分页插件的工具类
*/
public class PageHelperUtils{
/**
* 执行PageHelper分页的方法
* @param req 请求对象,继承PageParam类
* @param executePageHelper ExecutePageHelper的接口实现类
* @param <T> 泛型,需要返回结果的类型
* @return
*/
public static <T> PageInfo<T>execute(PageParam req,ExecutePageHelper executePageHelper){
//这里直接传入req,其实其中的值是有映射关系的,在PageParam中有讲到
return PageHelper.startPage(req).doSelectPageInfo(executePageHelper);
}
}
@Test
public void test4(){
SqlSessionTemplate sqlSessionTemplate = applicationContext.getBean(SqlSessionTemplate.class);
PatientMapper mapper = sqlSessionTemplate.getMapper(PatientMapper.class);
//分页的请求类,继承ParamReq
UserReq req=new UserReq();
PageInfo<Patient> pageInfo = PageHelperUtils.execute(req, mapper::selectAllPatient);
System.out.println(pageInfo.getList());
}