insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
则插入后得到的Author对象的id属性为新插入记录所得到的id值。
插入多行,传入一个Authors数组或集合,并返回自动生成的主键:
<insert id=“insertAuthor” useGeneratedKeys=“true”
keyProperty=“id”>
insert into Author (username, password, email, bio) values
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
对于不支持自动生成类型的数据库或可能不支持自动生成主键 JDBC 驱动来说,MyBatis 有另外一种方法来生成主键。使用insert语句的selectKey 子元素,selectKey例子如下(这里只是一个演示selectKey的例子,MySql数据本身可以支持自动生成主键)
SELECT LAST_INSERT_ID()
insert into college (name)
values (#{name,jdbcType=VARCHAR})
3.3 参数(Parameters):
select id, username, password
from users
where id = #{id}
这个简单的例子中,可以不用设置parameterType。如果参数是一个对象,例子如下:
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
其中User为参数,id、username、password均为User对象的属性。
参数也可以指定一个特殊的数据类型,如#{property,javaType=int,jdbcType=NUMERIC}
。
保留两位小数的例子:#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
但一般来说,仅需指定属性名,其他的事情 MyBatis 会自己去推断,最多你需要为可能为空的列名指定 jdbcType,例子如下:
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
默认情况下,使用#{}格式的语法会导致 MyBatis 创建预处理语句属性并安全地设置值(比如?)。这样做更安全,更迅速,通常也是首选做法
3.4 sql
:可被其他语句引用的可重用语句块
例子如下:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 SQL 片段可以被包含在其他语句中,例如:
select
,
from some_table t1
cross join some_table t2
3.5 Result Maps
resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果 集中取出数据的 JDBC 代码的那个东西, 而且在一些情形下允许你做一些 JDBC 不支持的事情。
select id, username, hashedPassword
from some_table
where id = #{id}
这个例子中id, username, hashedPassword属性映射到User对象的相应属性。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个 基本的 SQL 特性)来匹配标签
select
user_id as “id”,
user_name as “userName”,
hashed_password as “hashedPassword”
from some_table
where id = #{id}
也可以用resultMap来解决:
select user_id, user_name, hashed_password
from some_table
where id = #{id}
3.6 高级结果映射:
一个复杂查询的例子
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
其对应的resultMap如下:
在这个例子中涉及到的对象由Blog、Author、Post、Comment、Tag。
一个Blog对应一个Author,一个Post集合(一对多)。
Post集合中的每个Post对应一个Author、一个Comment集合、一个Tag集合、一个DraftPost(如果draft列的值为1的时候)。
构造方法:
对应下面这个对象的构造方法
public class User {
//…
public User(int id, String username) {
//…
}
//…
}
3.7 id & result:
id 表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现,特别是**缓存和嵌入结果映射(也就是联合映射) **。总的来说id就是用于标识对象,以方便缓存,在随后的对象组装、执行查询时,速度更快。
3.8 关联( ,一对一)
关联元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。见下面的使用resultMap映射对象。
加载关联对象,如上面的查询中,查询一个博客时同时加载与该博客对应的用户对象。可以有两种方式:
-
嵌套查询:通过执行另外一个SQL映射语句(产生N+1问题)
-
嵌套结果:使用嵌套结果映射。如上面的复杂SQL语句,一下就把多个表中的所有数据取出。也是我们比较熟悉的一种查询方式。
嵌套查询的一个例子:
SELECT * FROM BLOG WHERE ID = #{id}
SELECT * FROM AUTHOR WHERE ID = #{id}
既然嵌套查询会带来N+1性能问题,什么时候使用嵌套查询呢?
比如上面的selectBlog返回的Blog对象,包含一个Author对象。有的时候我们只需加载Blog对象,只有在需要的时候再加载Author对象。那么我们可以使用嵌套查询,只有在需要的时候再执行selectAuthor,当然这会执行两条select语句。这就是所谓的MyBatis的延迟加载。
MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。如一个Author有一个Post集合(n个post),如果你使用懒加载,然后在程序中遍历这个Post集合(可能是一个List),那么你还要执行n个selectPost语句。
3.9 延迟加载
例子如下:
1.首先需要在mybatis-config.xml中加入如下配置,才能使延迟加载功能生效
2.相应的mapper文件中加入如下设置:
<association property=“college” column=“college_id”
javaType=“College” select=“selectCollegeForStudent”/>
select * from student where id= #{id}
select * from College where id =#{id}
3.测试代码如下:
@Test
public void test0SelectStudentLazyLoadOneToOne(){
System.out.println(“TestCollege.test0SelectStudentLazyLoadOneToOne()”);
SqlSession session;
session = sqlSessionFactory.openSession();
int id = 1;
try {
System.out.println(“=测试LazyLoad=”);
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student student = mapper.selectStudentLazyLoad(id);
System.out.println(student.getName());
System.out.println(“=现在才开始加载College=”);
System.out.println(student.getCollege());
} finally {
session.close();
}
}
对Collection的延迟加载也适用。
4.如果全局打开延迟加载,但希望对个别方法进行积极加载,可以设置fetchType为eager:
<association property=“college” column=“college_id”
javaType=“College” select=“selectCollegeForStudent” fetchType=“eager”/>
**实验证明:**如果不进行任何设置,那么默认为积极加载。只能通过在 设置设置为全局懒加载,然后对个别方法设置为积极加载,如上例。
3.10 多表联合查询解决N+1性能问题
延迟加载会碰到N+1性能问题。碰到这种问题,我们一般直接使用嵌套结果方式(联合查询),例子如下。
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}
我们看到,只使用了一条select语句。就把两个对象的值一次性填入。
resultMap还支持使用前缀columnPrefix重用映射。具体例子见官网
3.11 集合 :
对应private List<Post> posts;
一个集合嵌套查询的例子:
SELECT * FROM BLOG WHERE ID = #{id}
SELECT * FROM POST WHERE BLOG_ID = #{id}
javaType也可不需要,也可简化为:<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
3.12 集合的嵌套结果(更常用)
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
P.id as post_id,
P.subject as post_subject,
P.body as post_body,
from Blog B
left outer join Post P on B.id = P.blog_id
where B.id = #{id}
3.13 鉴别器
对于某一列的不同值,可能对应不同类型的对象,可以使用<discriminator>
,如下属例子
一个简单的例子
详解见官方文档。
3.14 自动映射
一般情况可以直接列名映射为属性名(忽略大小写),如 ID列(数据库字段)->id属性(对象属性)。
通常数据库列使用大写单词命名,单词间用下划线分隔,如User_Name
;而java属性一般遵循驼峰命名法(userName)。 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase设置为true。
即在mybatis-config.xml中设置
`
还可以混合使用自动映射和配置映射(id和userName被自动映射,hashed_password 列将根据配置映射):
select
user_id as “id”,
user_name as “userName”,
hashed_password
from some_table
where id = #{id}
3.15 缓存
使用<cache/>
开启,默认情况下没有开启。
一个例子
<cache
eviction=“FIFO”
flushInterval=“60000”
size=“512”
readOnly=“true”/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。
详见官方文档。
具体例子见后面缓存。
4. 动态SQL(重点掌握)
5. Java API(简单了解)
专题1.使用resultMap映射对象(sql语句映射)
如果使用JDBC从数据库获取数据的一般流程:
-
创建Connection
-
使用PreparedStatement执行sql语句
-
使用ResultSet获取数据,并创建model对象,执行对象的setter方法为对象赋值。
-
关闭ResultSet、PreparedStatement、Connection
其中第2、3步,mybatis可使用配置文件来完成。
假设有如下sql语句:select e.*,s.id as siteId,s.name as siteName, s.longitude as longitude,s.latitude as latitude from t_equipment e, t_site s where e.sid = s.id
,相关的model对象如下:
public class Site {
private Integer id;
private String name;
private Double longitude;
private Double latitude;
…
}
public class EquipmentDetail {
private Integer id;
private Site site;//注意这里
private String name;
private String type;
private String version;
private String brand;
private String phonenumber;
…
}
传统的对象组装流程:
EquipmentDetail equipment = new EquipmentDetail();
Site site = new Site();
//从resultset中取指定列名的数据,执行对应的equipment的setter方法,
//从resultset中取指定列名的数据,执行对应的一堆site的setter方法
equipment.setSite(site);
//组装完毕
上面这个过程完全写好配置文件,让mybatis按照配置文件进行自动组装,而无需自己编写代码。
上面的过程主要包含几个关键信息: 1. 列名及其对应的属性名。 2.主键、外键信息。
对应的mybatis配置文件如下:
select e.*,s.id as siteId,s.name as siteName, s.longitude as longitude,s.latitude as latitude from t_equipment e, t_site s where e.sid = s.id
其中resultMap="EquipmentDetailResultMap"
表示当sql语句取到数据后可按照其配置信息来进行对象的组装。
实际上resultMap="EquipmentDetailResultMap"
就是代表对象组装的配置信息。这个配置信息可以在多个地方用到。比如下面这段配置信息也复用到了EquipmentDetailResultMap
select e.*,s.id as siteId,s.name as siteName, s.longitude as longitude,s.latitude as latitude from t_equipment e, t_site s where e.sid = s.id and e.id = #{id}
结论: resultMap用来描述组装数据到对象这样的一个过程,并且可以很方便的进行复用。这使我们无需使用硬编码的方式进行繁琐的组装对象的过程。只需要写好配置文件,mybatis就默默地按照配置文件的指示完成对象的组装。
参考
专题2.动态SQL
动态指定条件子句:
使用 <if>
:
select e.*,s.id as siteId,s.name as siteName, s.longitude as longitude,s.latitude as latitude from t_equipment e, t_site s where e.sid = s.id
and s.id = #{sid}
and e.name like ‘%${name}%’
也可以使用<where>
<select id=“findActiveBlogLike”
resultType=“Blog”>
SELECT * FROM BLOG
state = #{state}
AND title like #{title}
AND author_name like #{author.name}