Mybatis[6] - 映射文件

博由

在大概介绍完一些关于Mybatis Config的使用之后,我们进一步的了解Mapper Config文件的使用,大致可以分为:
1. cache-ref:缓存引用
2. cache:引入自定义的缓存
3. resultMap:结果集映射
4. select:对应SQL select
5. insert:对应SQL insert
6. update:对应SQL update
7. delete:对应SQL delete

映射文件

关系:--- Table <---> Mapper <---> Bean(Pojo)
mybatis是ORM框架,其中最大的一个问题在于如何简历javaBean与Table之间的关系,然后通过操作javaBean就可以操作DB,以屏蔽DB的差异问题等等,其实就是简单理解为:Mapper目的就是建立Table和Bean的一一对应关系。

解析原理

代码链路:

1. XmlConfigBuilder(parseConfiguration --> mapperElement) -> XmlMapperBuidler
2. XmlMapperBuidler(parse -> configurationElement) 
configurationElement 内部就是初始化mapper配置文件的操作,接下来个个击破

sql

这个比较简单,其实就是SQL模板,提高复用性。
1. 定义:
 <sql id="tableAll">
     SELECT * FROM ${table}
 </sql>
2. 使用
<select id="findById"
        parameterType="int"
        resultMap="userMap">
        <!-- include引用 -->
        <include refid="tableAll">
            <!-- 注入sql定义的${info} -->
            <property name="table" value="user"/>
        </include> WHERE  id = #{id}
    </select>

Cache

Mybatis的缓存结构
 * | -- Cache
 * | -- -- PerpetualCache    其实就是HashMap的实现
 * | -- -- -- BlockingCache  阻塞Cache,实际上就是加个锁的实现
 * | -- -- -- FifoCache      队列Cache,First in First Out
 * | -- -- -- LoggingCache   日志Cache
 * | -- -- -- WeaKCache      弱引用Cache
 * | -- -- -- TransactionalCache 事务Cache
 * | -- -- -- LRUCache       LRU Cache
 * | -- -- -- ScheduledCache 定时Cache
 * | -- -- -- SerializedCache 序列化Cache
 * | -- -- -- SoftCache       和Weak有点类似,引用Cache
 * | -- -- -- SynchronizedCache 同步Cache
 这些都是预定义的缓存类,通过装时器模式。
使用自定义缓存
1. implements Cache interface
2. Mapper Config 配置<cache></cache>
implements Cache Interface
public class HashMapCache implements Cache{

    // 缓存唯一标识
    private String id;

    // 缓存名字
    private String name;

    // k:主键ID;v:缓存内容
    private Map<Object, Object> cache = new HashMap<Object, Object>();

    public HashMapCache(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public void putObject(Object key, Object value) {
        cache.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return cache.get(key);
    }

    @Override
    public Object removeObject(Object key) {
        return cache.remove(key);
    }

    @Override
    public void clear() {
        cache.clear();
    }

    @Override
    public int getSize() {
        return cache.size();
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}
Mapper Config 配置
    <!--  
    type: 缓存类型,默认PerpetualCache(PERPETUAL)
    eviction: 缓存回收策略,默认LRU
    flushInterval: 缓存刷新间隔
    size: 缓存的大小
    readOnly: default false,是否只读
    blocking: default false,是否
    -->
    <cache type="com.mapper.caches.HashMapCache">
        <property name="name" value="testCache" />
    </cache>
缓存如何使用代码解析
XmlMapperBuilder(->cacheElement
                 ->builderAssisant.useNewCache)

useNewCache

public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build(); // build方法创
    // 创建之后添加cache到配置中
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }
单元测试

TestCase: 同一个Session中,无法使用到缓存

@Test
    public void testCacheSql(){

        SqlSession session = instance.getSession();

        long t1 = System.currentTimeMillis();
        User user1 = session.selectOne(User.class.getName() + ".findById", 5);
        System.out.println(System.currentTimeMillis() - t1);

        long t2 = System.currentTimeMillis();
        User user2 = session.selectOne(User.class.getName() + ".findById", 5);

        long t3 = System.currentTimeMillis();
        User user3 = session.selectOne(User.class.getName() + ".findById", 5);

        System.out.println(System.currentTimeMillis() - t3);
    }

TestCase2: 不同事务中,可以使用到缓存

@Test
    public void testMyCache(){
        // 这是的查询,在commit时,会将TransactionalCache中的key,value信息
        // 重新设置到LoggingCache->HashMapCache中。
        User user1 = UserDao.getInstance().findById(5);

        // 这样就可以使用到user1操作的cache结果了,这里查询时,不需要使用到TransactionalCache,
        // 而是在执行之前就使用了HashMapCache
        User user2 = UserDao.getInstance().findById(5);

        System.out.print(user1 + " :=====: " + user2);
    }
源码解析现象
HashMapCache 被 LoggingCache包装
public Cache build() {
    setDefaultImplementations();
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      // 注意这里使用LoggingCache来包装HashMapCache
      cache = new LoggingCache(cache);
    }
    return cache;
  }
TransactionalCache的诞生
  1. CachingExecutor.java
@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 在构建Configuraion的mappedStatement时,会将Cache设置
    Cache cache = ms.getCache();
    if (cache != null) {
    // 如果设置flushCache,此时会先刷新cache
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        // 在执行操作前,先查看cache是否存在
        // 但是这里不是先使用的loggingCache而是TransactionalCache
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 查询后,会将结果设置到cache中
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  1. TransactionalCacheManager
public class TransactionalCacheManager {

  private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();

  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }

  // 刚才在cachingExecutor.query中,在执行查询之前,选执行了
  // tcm.getObject(cache,key),就是这个方法
  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }

  // 注意每次都是根据LogginCache获取,都会获取到TransactionalCache
  // 这是我们看看TransactionalCache的putObject,并不是我们设想的
  // 会设置到LoggingCache中
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }

  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }

  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }

  // 获取cache
  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      // 第一次cache操作时,tcm不存在,会将loggingCache作为key,transactionalCache
      // 作为value执行操作。
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }
}
  1. TransactionalCache
private Cache delegate;
private boolean clearOnCommit;
private Map<Object, Object> entriesToAddOnCommit;
private Set<Object> entriesMissedInCache;

@Override
public void putObject(Object key, Object object) {
    // 不是使用了delegate.put而是将结果设置到了entriesToAddOnCommit中
    entriesToAddOnCommit.put(key, object);
}


@Override
public Object getObject(Object key) {
    // issue #116
    // 先从LoggingCache获取结果,如果hit了,  获取结果但是不是返回
    Object object = delegate.getObject(key);
    if (object == null) {
      entriesMissedInCache.add(key);
    }
    // issue #146
    // clearOnCommit才是关键,如果是已经提交了,那么返回null
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
}
// 关键: 只有事务结束后,才会把TransactionalCache的缓存刷到LoggingCache
// 因此TestCase1,无法使用缓存,也就会导致hits rate为0
// TestCase2,每个find都是独立的,第一查询commit之后,会把缓存存放在LogginCache
// 然后就可以直接使用自己的缓存了。
public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    // 在commit之后,会将信息刷到LogginCache
    flushPendingEntries();
    reset();
}

private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      // 这里就是刷的过程
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
 }
TransactionalCache的作用:
可能你会问为啥要加上TransactionalCache,这是为了保证事务数据的一致性设计的,如果我们不加设个Cache,那么直接将结果存放在LoggingCache,一旦出现事务错误,下次查询会查询到错误事务的结果,这是不合理的,只有在commit之后,commit是认为事务正确的标识,正确了才把数据刷到对应的Cache,不然会出现Cache错误。
Cache 转换结构:
1. HashMapCache 被 LoggingCache 包装;
2. TransactionalCacheManager.getObject(cache, key),会初次生成TransactionalCache
3. 第一次查询结果后,会把结果通过TransactionalCacheManager.putObject(cache,key,value) 设置到TransactionalCache的entriesToAddOnCommit中,而不是LogginCache中;
4. 如果没有进行commit操作,执行第二次重复查询实际上使用的缓存不是自定义的HashMapCache,而是TransactionalCache;如果要确保使用到自己的Cache,那么commit时,会将TransactionalCache中的entriesToAddOnCommit刷到LoggingCache中,也就刷到了HashMapCache了,下次查询就可以使用到。   

CacheRef

目的就是引用现成的cache,比较简单,例如:
<cache-ref namespace="com.mapper.pojo.Cache" /> 
简单看看源码

XmlMapperBuilder->cacheRefElement

private void cacheRefElement(XNode context) {
    if (context != null) {
      // 设置cacheRef的映射表<currentNamespace, namespace>
      // currentNamespace: cacheRef所在的namespace
      // cachRef->namespace: 是引用的Cache空间    configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
      CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
      try {
        // 这里就是解析cacheRef的过程,内部策略就是缓存的namesapce问题比较简单就不做详细介绍了。
        cacheRefResolver.resolveCacheRef();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteCacheRef(cacheRefResolver);
      }
    }
  }

ResultMap

ResultMap就是结果集的映射,一般的情况我们使用resultType就可以,但是存在一些情况resultType无法满足,这个时候就可以使用resultMap实现更加复杂多样的映射关系。
映射注入的方式
resultMap最大的目的在于结果集的映射,从Table到Bean是如何查询结果最终设置到Bean中的。主要有两种方式:
1. 基于属性注入(Setter/Getter)
2. 基于构造函数注入(Constrcutor)
Property 注入 Setter/Getter
<!-- 
    id: resultMap的唯一标识
    type: resultMap的结果映射类型
    extends: 继承,resultMap之间可以实现继承的
 -->
<resultMap id="userMap" type="User" extends="baseUserMap">
    <!-- 主键标识 
        property: 属性名(类中的)
        column: 对应table的列名
        javaType: property字段的类型
        jdbcType: column对应数据库的类型
        typeHandler: 指定的类型处理器,前面篇章有所介绍,这里不做详细介绍。
    -->
    <id property="id" column="id" javaType="int" jdbcType="INTEGER"/>
    <!-- result 非主键字段的标识,内部属性与ID一致 -->
    <result property="createdAt" column="created_at" />
    <result property="updatedAt" column="updated_at" />
</resultMap>

属性注入过程:

1. XmlMapperBuilder.resultMapElement 将resultMap信息加载到Configuration中,
其中一个resultMap标签对应一个ResultMap类,resultMap内部属性对应ResultMapping类;
2. 在加载配置文件之后,在执行query时,出触发PreparedStatementHandler.query -> DefaultResultSetHandler.handleResultSets -> DefaultResultSetHandler.handleResultSet ->
DefaultResultSetHandler.handleRowValues ->
DefaultResultSetHandler.handleRowValuesForSimpleResultMap -> 
DefaultResultSetHandler.getRowValue ->
DefaultResultSetHandler.applyAutomaticMappings -> 
DefaultResultSetHandler.createAutomaticMappings | 
DefaultResultSetHandler.applyPropertyMappings 这个方法中,进行了属性注入
Constructor 构造函数注入
 <resultMap id="baseUserMap" type="BaseUser">
        <!-- 构造方法注入数据,是通过目标类的构造方法注入的 -->
        <constructor>
            <idArg column="id" javaType="int" jdbcType="INTEGER" />
            <arg column="name" javaType="string" jdbcType="VARCHAR" />
        </constructor>
 </resultMap>

构造函数注入过程:

和属性注入有点相似,但是DefaultResultSetHandler.createResultObject进行了判断,如何创建。
DefaultResultSetHandler.getRowValue ->
DefaultResultSetHandler.createResultObject -> 在注入属性之前先进行构造函数注入,然后在属性注入。
这是合理的,都是现有对象,然后在设置属性。
实体映射关系
实体的映射关系主要有:
1. OneToOne
2. OneToMany | ManyToOne
3. ManyToMany
OneToOne
一对一的关系,案例:夫妻关系

SQL:

create table husband (
    `id` int not null auto_increment,
    `name` varchar(255) not null default '' comment '姓名',
    `wife_id` int not null default 0 comment '妻子ID',
    `created_at` datetime not null default '1970-01-01' comment '创建时间',
    `updated_at` datetime not null default '1970-01-01' comment '更新时间',
    primary key(`id`),
    unique key `uk_wife_id` (`wife_id`)
)ENGINE INNODB charset=utf8 COMMENT '丈夫表';


create table wife (
    `id` int not null auto_increment,
    `name` varchar(255) not null default '' comment '姓名',
    `created_at` datetime not null default '1970-01-01' comment '创建时间',
    `updated_at` datetime not null default '1970-01-01' comment '更新时间',
    primary key(`id`)
)ENGINE INNODB charset=utf8 COMMENT '妻子表';

POJO:

public class Husband implements Serializable {

    private int id;

    private String name;

    private int wife_id;

    private Date createdAt;

    private Date updatedAt;

    private Wife wife;

    public int getWife_id() {
        return wife_id;
    }

    public void setWife_id(int wife_id) {
        this.wife_id = wife_id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }

    @Override
    public String toString() {
        return "Husband{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", wife_id=" + wife_id +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                ", wife=" + wife +
                '}';
    }
}

public class Wife implements Serializable {

    private int id;

    private String name;

    private Date createdAt;

    private Date updatedAt;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }


    @Override
    public String toString() {
        return "Wife{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                '}';
    }
}

Mapper

1. Husband.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.pojo.Husband">

    <resultMap id="husbandMap" type="Husband" >
        <id property="id" column="id"/>
        <result property="name" column="name" />
        <result property="wife_id" column="wife_id"/>
        <result property="createdAt" column="created_at" />
        <result property="updatedAt" column="updated_at" />
        <!-- 建立关联关系 -->
        <association property="wife"
                     column="wife_id"
                     fetchType="lazy"
                     select="com.mapper.pojo.Wife.findById"
                     javaType="Wife"/>
    </resultMap>

    <select id="findById" parameterType="int" resultMap="husbandMap" >
        SELECT * FROM husband WHERE id=#{id}
    </select>

</mapper>

2. Wife.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.pojo.Wife">
    <resultMap id="wifeMap" type="Wife" >
        <id property="id" column="id"/>
        <result property="name" column="name" />
        <result property="createdAt" column="created_at" />
        <result property="updatedAt" column="updated_at" />
    </resultMap>
    <select id="findById" parameterType="int" resultMap="wifeMap" >
        SELECT * FROM wife WHERE id=#{id}
    </select>
</mapper>
OneToMany|ManyToOne
1对多或者多对1的问题,案例:一个教师多个学生

SQL:

create table teacher (
    `id` int not null  auto_increment,
    `name` varchar(255) not null default '' comment '姓名',
    `created_at` datetime not null default '1970-01-01' comment '创建时间',
    `updated_at` datetime not null default '1970-01-01' comment '更新时间',
    primary key(`id`)
)ENGINE INNODB charset=utf8 COMMENT '教师表';

create table student (
    `id` int not null auto_increment,
    `name` varchar(255) not null default '' comment '姓名',
    `tid` int not null default 0 comment '教师ID',
    `teacher_name` varchar(255) not null default '' comment '教师名称',
    `created_at` datetime not null default '1970-01-01' comment '创建时间',
    `updated_at` datetime not null default '1970-01-01' comment '更新时间',
    primary key(`id`)
)ENGINE INNODB charset=utf8 COMMENT '学生表';

POJO:

1. Student.java
public class Student implements Serializable {

    private int id;

    private String name;

    private int tid;

    private String teacherName;

    private Date createdAt;

    private Date updatedAt;

    private Teacher teacher;

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTid() {
        return tid;
    }

    public void setTid(int tid) {
        this.tid = tid;
    }

    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", tid=" + tid +
                ", teacherName='" + teacherName + '\'' +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                ", teacher=" + teacher +
                '}';
    }
}
2. Teacher.java
public class Teacher implements Serializable {

    private int id;

    private String name;

    private Date createdAt;

    private Date updatedAt;

    private List<Student> students = new ArrayList<Student>();

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                ", students=" + students +
                '}';
    }
}

Mapper:

1. Student.xml
<mapper namespace="com.mapper.pojo.Student">

    <resultMap id="studMap" type="Student">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="tid" column="tid" />
        <result property="teacherName" column="teacher_name" />
        <result property="createdAt" column="created_at" />
        <result property="updatedAt" column="updated_at" />
        <association property="teacher" column="tid" select="findByTid"/>

    </resultMap>

    <select id="findById" parameterType="int" resultMap="studMap">
        SELECT * FROM student WHERE id = #{id}
    </select>

    <select id="findByTid" parameterType="int" resultType="Teacher" >
        SELECT * FROM teacher WHERE id = #{tid}
    </select>

</mapper>
2. Teacher.xml
<mapper namespace="com.mapper.pojo.Teacher">

    <resultMap id="teacherDtoMap" type="Teacher" >
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="createdAt" column="created_at" />
        <result property="updatedAt" column="updated_at" />
        <!-- 关联关系:OneToMany -->
        <collection property="students" ofType="Student" >
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid" />
            <result property="teacherName" column="teacher_name" />
            <result property="createdAt" column="s_created_at" />
            <result property="updatedAt" column="s_updated_at" />
        </collection>

    </resultMap>

    <select id="findById" parameterType="int" resultMap="teacherDtoMap">
        SELECT
            teacher.id AS id,
            teacher.name AS name,
            teacher.created_at AS created_at,
            teacher.updated_at AS updated_at,
            student.id AS sid,
            student.name AS sname,
            student.tid AS tid,
            student.teacher_name AS teacher_name,
            student.created_at AS s_created_at,
            student.updated_at AS s_updated_at
        FROM teacher join student ON teacher.id = student.tid
        WHERE teacher.id=#{id}
    </select>
</mapper>
貌似Mybatis对象关联关系没Hibernate做的好,不过实际在开发中用的也比较少,感觉不支持双向绑定。多对多貌似实现不了,下次深刻研究下,专题研究,这里不做概述。

insert|select|update|delete

这个就比较简单了,直接使用:
<insert><select><update><delete>四个标签就可以了,

比较简单:

   <select id="findById"
            parameterType="int"
            resultMap="userMap"
            >
        <include refid="tableAll">
            <property name="table" value="user"/>
        </include> WHERE  id = #{id}
    </select>

    <!-- insert操作 -->
    <insert id="add" parameterType="User">
        INSERT INTO user VALUES(null, #{name}, #{createdAt},#{updatedAt});
    </insert>

    <!-- update更新操作 -->
    <update id="update" parameterType="User">
        UPDATE user SET name=#{name},created_at=#{createdAt}, updated_at=#{updatedAt}
        WHERE id=#{id}
    </update>

    <!-- 删除操作 -->
    <delete id="deleteById" parameterType="int" >
        DELETE FROM user
        WHERE id = #{id}
    </delete>

GitHub地址

branch v1.6: https://github.com/wzpthq/csdn_mybatis.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值