您的位置:

Mybatis延迟加载详解

一、通过select语句实现延迟加载

在Mybatis中,延迟加载可以使用select语句来实现。假设我们有两个表,一个是用户表,一个是订单表,用户表中保存了用户的基本信息,订单表中保存了用户的订单信息。这两个表之间是一对多关系,即一个用户可以拥有多个订单。

在使用Mybatis查询一条用户记录时,如果需要同时查询该用户的所有订单信息,需要在Mapper文件中定义如下的select语句:

<select id="getUserWithOrders" resultMap="userMap">
  select * from user where id = #{userId}
  select * from order where user_id = #{userId}
</select>

这样的查询会产生一个问题:在查询用户信息时,同时也会查询该用户的所有订单信息,如果该用户拥有数千条订单信息,那么查询的效率就会非常低。此时,可以通过使用Mybatis的延迟加载来解决这个问题。

二、Mybatis延迟加载是什么

Mybatis中的延迟加载,指的是当我们查询一条记录时,并不会立即查询该记录的关联记录,而是当我们需要访问该关联记录时,才会进行查询。这种方式可以有效提高查询效率。

三、Mybatis延迟加载的原理面试题

Mybatis的延迟加载原理是什么?

在Mybatis中,延迟加载是通过动态代理实现的。当我们调用一个延迟加载的关联对象时,Mybatis会首先判断该对象是否为空,如果为空,则调用Mapper接口中的方法,查询该关联对象。在查询完成后,将查询结果封装到代理对象中,并返回该代理对象。

四、Mybatis延迟加载默认

Mybatis的延迟加载默认是关闭的。如果需要启用延迟加载,则需要在配置文件中开启相应的延迟加载项。

五、Mybatis延迟加载的实现方式

Mybatis中的延迟加载,有两种实现方式:

1. 延迟加载关联对象

延迟加载关联对象,指的是只有在需要访问关联对象时,才会进行查询。这种方式可以避免一次性查询大量数据,提高查询效率。

2. 延迟加载属性

延迟加载属性,指的是只有在需要访问属性时,才会进行查询。这种方式可以避免查询出大量无用的数据,提高查询效率。

六、Mybatis延迟加载的原理

Mybatis中的延迟加载,通过动态代理的方式实现。在查询时,如果需要延迟加载某个关联对象或属性,Mybatis会将该对象或属性封装到一个代理对象中,并返回该代理对象。

当我们访问代理对象时,Mybatis会先判断代理对象中是否已经加载了该关联对象或属性,如果没有加载,则调用Mapper接口中的方法,查询该关联对象或属性。在查询完成后,将查询结果封装到代理对象中,并返回该代理对象。下次访问该关联对象或属性时,直接返回代理对象中的值,不再进行查询。

七、Mybatis延迟加载怎么配置

Mybatis中的延迟加载需要在配置文件中进行配置。可以通过在Mapper文件中使用select标签,并指定fetchType属性的方式,来开启延迟加载。

例如,在查询用户信息时,我们可以通过以下方式开启延迟加载:

<resultMap id="userMap" type="user">
  <result property="id" column="id"/>
  <result property="name" column="name"/>
  <result property="orders" column="id" select="getOrdersByUserId" fetchType="lazy"/>
</resultMap>

<select id="getUserById" resultMap="userMap">
  select * from user where id = #{id}
</select>

<select id="getOrdersByUserId" resultMap="orderMap">
  select * from order where user_id = #{id}
</select>

在上面的代码中,我们使用了resultMap来定义查询结果的映射关系。其中,fetchType属性指定了关联对象的延迟加载方式。lazy表示延迟加载,即只有在需要访问该关联对象时,才会进行查询。

八、Mybatis延迟加载没反应

如果在使用Mybatis的延迟加载时,发现没有生效,可能是由于以下几个原因:

1. 配置文件中未开启延迟加载

在使用延迟加载时,需要在配置文件中开启相应的延迟加载项。如果未开启,延迟加载是不会生效的。

2. 关联对象已经被加载

如果一个关联对象已经被加载过了,则再次访问该对象时,不会触发延迟加载。在这种情况下,可以考虑使用二级缓存。

3. 关联对象不存在

如果一个关联对象不存在,则再次访问该对象时,也不会触发延迟加载。在这种情况下,需要检查关联对象的外键值是否正确。

九、Mybatis二级缓存

Mybatis中的二级缓存,是用来缓存Mapper文件中的查询结果的。在需要查询相同结果集时,如果已经存在于缓存中,就可以直接从缓存中获取结果,而不需要再次进行查询。这样可以有效提高查询效率。

在使用Mybatis的延迟加载时,可以使用二级缓存来避免多次查询同一条记录的关联对象。当一个关联对象已经被加载过了,可以将该对象缓存到二级缓存中。在下次访问该关联对象时,就可以直接从缓存中获取,而不再进行查询。

在Mybatis中,可以通过在配置文件中开启二级缓存和指定缓存类型来开启二级缓存。例如:

<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

在上面的代码中,我们开启了二级缓存,并指定了缓存类型为Ehcache。如果需要使用其他类型的缓存,可以在type属性中指定相应的缓存类型。

十、Mybatis延迟加载附源码

以下是使用Mybatis实现延迟加载的源码:

public interface UserMapper {
  User getUserWithOrders(long userId);
}

// Mapper文件中的查询语句
<select id="getUserWithOrders" resultMap="userMap">
  select * from user where id = #{userId}
  select * from order where user_id = #{userId}
</select>

public class UserMapperImpl implements UserMapper {
  private final SqlSession sqlSession;
  private final OrderMapper orderMapper;

  public UserMapperImpl(SqlSession sqlSession, OrderMapper orderMapper) {
    this.sqlSession = sqlSession;
    this.orderMapper = orderMapper;
  }

  @Override
  public User getUserWithOrders(long userId) {
    User user = sqlSession.selectOne("getUserById", userId);
    user.setOrders(() -> orderMapper.getOrdersByUserId(userId));
    return user;
  }
}

public interface OrderMapper {
  List<Order> getOrdersByUserId(long userId);
}

// Mapper文件中的查询语句
<select id="getOrdersByUserId" resultMap="orderMap">
  select * from order where user_id = #{userId}
</select>

public class OrderMapperImpl implements OrderMapper {
  private final SqlSession sqlSession;

  public OrderMapperImpl(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }

  @Override
  public List<Order> getOrdersByUserId(long userId) {
    return sqlSession.selectList("getOrdersByUserId", userId);
  }
}

public class User {
  private long id;
  private String name;
  private List<Order> orders;

  // getters and setters
}

public class Order {
  private long id;
  private long userId;
  private String content;

  // getters and setters
}

public interface LazyLoader<V> {
  V load();
}

public class LazyObject<T> implements Serializable {
  private static final long serialVersionUID = 1L;

  private final LazyLoader<T> loader;
  private T value;

  public LazyObject(LazyLoader<T> loader) {
    this.loader = loader;
  }

  public T getValue() {
    if (value == null) {
      value = loader.load();
    }
    return value;
  }
}

public class LazyList<T> extends ArrayList<T> implements Serializable {
  private static final long serialVersionUID = 1L;

  private final LazyLoader<List<T>> loader;

  public LazyList(LazyLoader<List<T>> loader) {
    this.loader = loader;
    add(null);
  }

  @Override
  public T get(int index) {
    T value = super.get(index);
    if (value == null) {
      List<T> list = loader.load();
      if (list != null && list.size() > index) {
        value = list.get(index);
        set(index, value);
      }
    }
    return value;
  }
}