mybatis基础综合/常见面试题

概念/作用:

持久层框架,通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api 打交道,就可以完成对数据库的持久化操作。

数据源环境environment可以配置多个

mapper文件里面配置了多个select/insert/update/delete sql语句,取用的时候是采用标识:namespace+id

默认事务不提交

Mybatis----多表操作

 

一对一查询,一个用户只有一个订单

对应的sql语句:select *  from orders o,user u where o.uid=u.id;

user表和orders表连接,自然就需要user+order两个对象来映射返回的所有列

<mapper namespace="com.mapper.OrderMapper">

    <resultMap id="orderMap" type="com.domain.Order">

    <!--手动指定字段与实体属性的映射关系 column: 数据表的字段名称 property:实体的属性名称 -->

     <id column="oid" property="id"></id>

     <result column="ordertime" property="ordertime"></result>

     <result column="total" property="total"></result>



    //主键使用id标签

    <id column="id" property="id"></id>       

    <result column="uid" property="user.id"></result>

        <result column="username" property="user.username"></result>

        <result column="password" property="user.password"></result>

        <result column="birthday" property="user.birthday"></result>

    </resultMap>

    <select id="findAll" resultMap="orderMap">

        select * from orders o,user u where o.uid=u.id

    </select>

</mapper>

 

注解配置

首先核心配置文件中:

<mappers>
    <!--扫描使用注解的类所在的包-->
    <package name="com.mapper"></package>
</mappers>
 

在接口中进行sql语句的映射配置

public interface UserMapper {

    @Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
    public void save(User user);

    @Update("update user set username=#{username},password=#{password} where id=#{id}")
    public void update(User user);

    @Delete("delete from user where id=#{id}")
    public void delete(int id);

    @Select("select * from user where id=#{id}")
    public User findById(int id);

    @Select("select * from user")
    public List<User> findAll();

    @Select("select * from user")
    @Results({
            @Result(id=true ,column = "id",property = "id"),
            @Result(column = "username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(
                    property = "orderList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.mapper.OrderMapper.findByUid")
            )
    })
    public List<User> findUserAndOrderAll();
	上面等价与
	先在user表中查出uid 然后 在订单表中查询 select * from order where uid=user.uid

    @Select("SELECT * FROM USER")
    @Results({
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(
                    property = "roleList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid")
            )
    })
    public List<User> findUserAndRoleAll();


}

mybatis常见面试题:

具体问题

1、#{}和${}区别

都属于占位符

#{}是预编译处理 使用prepareStatedment的参数设置方法;$ {}是字符串替换,参数传递

#{} :mybatis内部会创建配prepareStatedment 使用#{}格式的语法在mybatis中使用prepareStatedment语句来安全的设置值; MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。

${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver。

#{} sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为?号, sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()。

预编译是:指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。

 

2、mapper.xml 标签有哪些?resultMap和resultType的区别?

        常用的<select>  <delete>  <insert>  <update>,除此之外:

<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态 sql 的 9 个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为 sql 片段标签,通过<include>标签引入 sql 片段,<selectKey>为不支持自增的主键生成策略标签。

   resultMap 是一种"查询结果集---Bean对象”属性名称映射关系,列和bean对象的属性的映射,一般适用于多表连接

 

Resulttype---一般适用于pojo(简单对象)类型数据,简单的单表查询

如果配置成类,一般映射会遵循约定:要求Bean对象字段名和查询结果集的属性名相同(可以大小写不同,大小写不敏感)。因为这个自动映射,可以省略调resultMap进行属性名映射。也可以是int long这种pojo类型

3.dao接口的底层原理(如何返回实现类并且封装好结果集合的)?

通过动态代理实现接口的实现类,在mapperPorxy(实现invocationHandler接口)中的invoke中调用sql语句:mapperMethod.execute(sqlSession, args);

===》会进行paramname解析 比如将#{}解析为?

4、MyBatis 是否可以映射 Enum 枚举类?

答:MyBatis 可以映射枚举类,不单可以映射枚举类,MyBatis 可以映射任何对象到表的一列上。映射方式为自定义一个 TypeHandler,实现 TypeHandler 的 setParameter()和 getResult()接口方法。TypeHandler 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 setParameter()和 getResult()两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。

实现自定义的typeHandler: 继承BaseTypeHandler<T> ---》 Mybatis就是依赖泛型参数<T>,获得泛型参数Class对象,再与反射获得的bean属性Class,进行一一对应的

除了上面的“智能”绑定外,我们还可以手动绑定TypeHandler。

<result property="phone" column="phone" typeHandler="com.mybatis3.typehandlers.PhoneTypeHandler"/>

8、typehandler

mybatis默认提供的typehandler:

比如:

 public TypeHandlerRegistry() {

    register(Boolean.class, new BooleanTypeHandler());

    register(boolean.class, new BooleanTypeHandler());

    register(JdbcType.BOOLEAN, new BooleanTypeHandler());

    register(JdbcType.BIT, new BooleanTypeHandler());

 register(JdbcType.VARCHAR, new StringTypeHandler());

自定义注册:

register(typeReference.getRawType(), typeHandler);

.getRawType()就是返回 泛化类型<T>

 

9、mybatis可以分页吗?如何?

Mybatis可以通过传递RowBounds对象,来进行数据库数据的分页操作,然而遗憾的是,该分页操作是对ResultSet结果集进行分页,也就是人们常说的逻辑分页,而非物理分页。

Rowbounds两个属性:offset + limit

offset就是从哪里开始读,最多读limit行

实现:

跳转到offset位置:

for (int i = 0; i < rowBounds.getOffset(); i++) {
    rs.next();
}




private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

10、mybatis如何返回在执行insert时生成的自增主键?

在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象。

。Mybatis是对JDBC的封装,其Jdbc3KeyGenerator类,就是使用下面的原理,来返回数据库生成的主键值的。

 

Class.forName("com.mysql.jdbc.Driver");

 

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "123");

conn.setAutoCommit(false);

PreparedStatement pstm = conn.prepareStatement("insert into students(name, email) values(?, ?)",

Statement.RETURN_GENERATED_KEYS);

 

pstm.setString(1, "name1");

pstm.setString(2, "email1");

pstm.addBatch();

pstm.setString(1, "name2");

pstm.setString(2, "email2");

pstm.addBatch();

pstm.executeBatch();

// 返回自增主键值

ResultSet rs = pstm.getGeneratedKeys();

while (rs.next()) {

Object value = rs.getObject(1);

System.out.println(value);

}

conn.commit();

rs.close();

pstm.close();

conn.close();

 

output:

246

247

 

Mapper.Xml配置方式。

 

<insert id="insertStudents" useGeneratedKeys="true" keyProperty="studId" parameterType="Student">

 

主键赋值到keyproperty

 

11、如何批量插入List<student>?

 

<insert id="insertStudents" useGeneratedKeys="true" keyProperty="studId" parameterType="java.util.ArrayList">

INSERT INTO

STUDENTS(STUD_ID, NAME, EMAIL, DOB, PHONE)

VALUES

<foreach collection="list" item="item" index="index" separator=",">

                (#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone})

            </foreach>

</insert>

 

此时返回主键id列表为null

 

 

11、Mybatis之foreach批量insert,返回主键id列表(修复Mybatis返回null的bug)

使用simpleExectuor/reuse可以返回

使用batch返回的是null

因为batch:如果传递的是List<Student>,那么,将包装为一个Map<String, Collection>对象

SimpleExecutor和ReuseExecutor可以正确返回foreach批量插入后的id列表的原理:

getParameters()方法,会再次处理参数类型,拆封map,获取map中的vaule 即collection对象

BatchResult又把Map<String, List<Student>>放到List中,于是,参数对象数据结构就变成了List<Map<String, List<Student>>>。

 

Java接口是否继承Object类?

https://dslztx.github.io/blog/2018/04/22/Java%E6%8E%A5%E5%8F%A3%E6%98%AF%E5%90%A6%E7%BB%A7%E6%89%BFObject%E7%B1%BB/

看似是继承的,因为一个接口可以直接调用hashcode toString方法 可以通过编译

但是如果接口继承了object类 接口就不能实例化

所以是,虚拟机制造了“继承的假象”。虚拟机会在顶层接口(没有父接口的接口)中自动定义一系列对应于Object类中“public”方法的虚方法,除非已经得到了显式定义,这点跟虚拟机会自动定义一个默认构造器类似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值