MyBatis学习笔记

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映射对象

加载关联对象,如上面的查询中,查询一个博客时同时加载与该博客对应的用户对象。可以有两种方式:

  1. 嵌套查询:通过执行另外一个SQL映射语句(产生N+1问题)

  2. 嵌套结果:使用嵌套结果映射。如上面的复杂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从数据库获取数据的一般流程:

  1. 创建Connection

  2. 使用PreparedStatement执行sql语句

  3. 使用ResultSet获取数据,并创建model对象,执行对象的setter方法为对象赋值。

  4. 关闭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就默默地按照配置文件的指示完成对象的组装。

参考

Mybatis 高级结果映射 ResultMap Association Collection

专题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}

### 关于 MyBatis 学习教程与笔记 #### 创建数据库表结构 为了更好地理解如何使用 MyBatis 进行操作,先定义一个简单的 `blog` 表来存储博客的相关信息。此表包含了博客 ID、标题、作者名、创建时间浏览次数等字段。 ```sql CREATE TABLE `blog`( `id` VARCHAR(50) NOT NULL COMMENT '博客 id', `title` VARCHAR(100) NOT NULL COMMENT '博客标题', `author` VARCHAR(30) NOT NULL COMMENT '博客作者', `create_time` DATETIME NOT NULL COMMENT '创建时间', `views` INT(30) NOT NULL COMMENT '浏览量' ) ENGINE=INNODB DEFAULT CHARSET=utf8; ``` 上述 SQL 语句用于创建名为 `blog` 的数据表[^2]。 #### 编写实体类 当构建应用程序时,通常会有一个对应的 Java 类代表每一张数据库表格。对于上面提到的 `blog` 表格而言,应该存在相应的 Blog 实体类: ```java public class Blog { private String id; private String title; private String author; private Date createTime; private Integer views; // Getters and Setters... } ``` 这里需要注意的是,在设计实体类的时候应当遵循一些最佳实践原则,比如尽量采用包装类型而非基本数据类型,并确保实体类中的属性名称以及其数据类型需同数据库表内的保持一致[^3]。 #### 定义 Mapper 接口 接着就是定义映射器接口(Mapper Interface)。这些接口用来描述对特定对象的操作行为而无需关心具体的实现细节。下面是一个非常基础的例子——查询所有用户的表: ```java import java.util.List; public interface UserMapper { /** * 查询所有的用户. * * @return 用户集合 */ List<User> findAll(); } ``` 这段代码展示了怎样去声明一个可以返回多个用户记录的方法。 #### 配置 XML 文件 最后一步是在 resources 资源目录下建立 XML 映射文件 usermapper.xml 来指定具体执行哪些 SQL 命令。这使得开发者能够轻松地管理复杂的 CRUD 操作而不必硬编码到程序逻辑里边。 ```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.example.mapper.UserMapper"> <!-- Select all users --> <select id="findAll" resultType="com.example.model.User"> SELECT * FROM users </select> </mapper> ``` 以上内容概括了有关 MyBatis 使用的基础知识点,包括但不限于环境设置、SQL 动态处理机制等方面的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值