在使用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()); }