文章目录
resultType=xx.xxx.xx
,但是我们可以为类指定别名,那么就可以直接使用别名,避免全类名冗余【不推荐使用】
<typeAliases>
<package name="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}")
Patient selectByIptNumAndInhos(@Param("iptNum") String iptNum, @Param("status") String status);
@Select("select * from patient_info where ipt_num=#{param1} and status=#{param2}")
Patient selectByIptNumAndInhos(String iptNum,String status);
@Select("select * from patient_info where ipt_num=#{iptNum} and status=#{status}")
Patient selectByIptAndInhosNumMap(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
这个注解指定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;
}
<resultMap id="baseResultMap" type="cn.tedu.domain.Dept">
<id column="id" property="id" />
<result column="name" property="name"/>
<collection property="patients" ofType="cn.tedu.domain.Patient">
<id column="pid" property="id"/>
<result column="userId" property="userId"/>
<result column="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}
<select id="selectById" resultMap="ByStepResultMap">
SELECT
*
FROM
dept_info
WHERE
id =#{id,jdbcType=INTEGER}
</select>
@Select("SELECT * from patient_info where dept_id=#{deptId}")
List<Patient> selectByDeptId(Integer deptId);
<!--分步查询科室信息和患者信息-->
<resultMap id="ByStepResultMap" type="cn.tedu.domain.Dept">
<id column="id" property="id" />
<result column="name" property="name"/>
<!--ofType:指定级联属性的类型-->
<!--select:指定分步查询患者信息的方法,全类名+方法名-->
<!--column:指定查询科室获取的结果的哪一个字段作为查询患者方法的参数,可以指定多个
如果指定多个,那么需要将参数封装成map,比如column="{key1=column1,key2=column2}"
-->
<!--fetchType:在开启全局延迟加载的时候设置是否延迟加载,默认是延迟加载,可以设置为eager表示不延迟加载-->
<collection property="patients" ofType="cn.tedu.domain.Patient"
select="cn.tedu.mapper.PatientMapper.selectByDeptId"
column="id">
</collection>
</resultMap>
<settings>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
_parameter
:代表整个参数,如果是一个参数就表示这个参数,如果是多个参数,此时的参数会被封装成一个Map,那么_parameter此时就代表整个Map_databaseId
:如果配置了databaseIdProvider标签,就代表当前数据库的别名@Select("SELECT * from patient_info where dept_id=#{_parameter}")
List<Patient> selectByDeptId(Integer deptId);
_parameter
判断是否为空,如下:<if test="_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
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 根据下标获取指定列的值
* @param rs ResultSet结果集
* @param columnIndex 下标
* @return
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
* 调用存储过程的获取返回值的时候
* @param cs
* @param columnIndex
* @return
* @throws SQLException
*/
T getResult(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 AuthTypeHandler extends 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>
<package name="cn.tedu.typehandler"/>
</typeHandlers>
<insert id="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>
<resultMap id="BaseResultMap" type="cn.tedu.domain.Admin">
<id column="id" property="id"/>
<result column="name" property="name" />
<result column="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
*/
Object intercept(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 FirstPlugin implements 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>
<plugin interceptor="cn.tedu.plugin.FirstPlugin">
<property name="id" value="11"></property>
</plugin>
</plugins>
SystemMetaObject.forObject(target)
:可以获取目标类中所有的属性的值metaObject.getValue("mappedStatement.id")
:可以使用xxx.xxx层层的获取属性的值metaObject.setValue(name, value)
:设置属性的值<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
@MapperScan
注解自动扫描Mapper接口SqlSessionFactoryBean
中的配置中设置xml文件的位置和全局配置文件中的位置SqlSessionTemplate
的注入能够让我们很方便的获取一个SqlSession和Mapper,一旦注入之后,所有的获取Mapper的代理对象都会执行其中的getMapper方法获取,因此如果这里设置了批量处理,那么改变是全局的。/**
* 配置类
* @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 ExecutePageHelper extends 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());
}