MyBatis 编程基础
1.1. MyBatis 基本步骤
Step01:创建maven 桌面项目(Java 项目)
Step02:添加mybatis依赖以及mysql驱动依赖
Step03:创建mybatis 配置文件,映射文件
Step04:配置数据访问(配置文件),SQL映射(映射文件)
Step05:创建MyBatis API(例如SqlSession)对象,执行SQL操作.
常见问题及解决办法:
1) 数据库连不上,可能存在哪些问题?
a) 检测url,用户名,密码
b) 检测端口号(port)
c) id地址是否能ping 通
d) 检测驱动程序(有可能依赖的驱动程序与数据库版本不兼容)
2) mybatis 配置文件的名字有要求吗?(只要符合标识符规范即可)
3) mybatis 配置文件是要对哪些信息进行配置呢?
4) mybatis 映射文件中主要用于定义哪些内容?(sql元素)
5) MyBatis 执行SQL操作时的一个基本过程是怎样的?
a) step01:MyBatis API调用JDBC API
b) step02:JDBC API 调用数据库驱动程序API
1.2. MyBatis 编程实现
1.2.1. 数据准备
创建数据库,并在库中创建表
create database cgb1711 character set utf8;
打开数据库
use cgb1711;
创建表
create table blog(
id int primary key auto_increment,
title varchar(200) not null,
content varchar(500) not null,
createdTime date
) engine=innoDB;
向表中写入数据
insert into blog values (null,'ta','ta...',now());
insert into blog values (null,'tb','tb...',now());
1.2.2. 创建Maven桌面项目
创建maven 桌面项目,并添加依赖(mybatis,mysql驱动)
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- 添加mysql驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- 添加junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
注意:
1) 添加依赖时注意groupId的选择?(选正规的)
2) 添加依赖以后pom.xml文件假如有错什么原因? 检测网络,检测maven配置(setting.xml)
1.2.3. 添加配置及映射文件
在src/main/resources目录下创建配置文件mybatis-configs.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置初始化环境(连接) -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///cgb1711"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 配置mapper文件路径 -->
<mappers>
<mapper resource="mapper/BlogMapper.xml"/>
</mappers>
</configuration>
说明:
1)配置文件的头可从官方文档进行拷贝.
2)配置文件元素没有提示是什么原因? 对应的dtd文件取不到
step02:在src/main/resources/mapper目录下创建映射文件BlogMapper.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.jt.blog.BlogDao">
</mapper>
说明:
1) 映射文件的命名空间用于约束元素id的唯一性.
2) 映射文件的格式最好是包结构形式
3) 映射文件内部可以定义很多元素,每个元素必须有一个唯一id,例如select
在mapper文件中添加如下元素:
查询所有blog数据
<select id="findBlogs" resultType="map">
select * from blog
</select>
根据id查询blog数据
<select id="findBlogById" resultType="map">
select *
from blog
where id=#{id}
</select>
限制查询blog元素
<select id="findPageBlogs"
resultType="map">
select * from blog
limit #{array[0]},#{array[1]}
</select>
向表中插入数据元素定义
<insert id="insertObject">
insert into blog
(id,title,content,createdTime)
values
(null,#{array[0]},#{array[1]},now())
</insert>
修改表中数据元素定义
<update id="updateObject">
update blog
set title=#{array[0]},
content=#{array[1]}
where id=#{array[2]}
</update>
删除表中元素元素定义
<delete id="deleteObject">
delete
from blog
where id=#{id}
</delete>
1.2.4. 编写代码执行查询测试
基于BlogMapper.xml文件中元素的定义,添加测试类及相关方法.
编写测试类并添加测试方法
public class TestBlog01 {
private SqlSessionFactory factory;
}
测试类中添加初始化factory的方法
@Before
public void init()throws IOException{
//初始化SqlSessionFactory
factory=new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(
"mybatis-configs.xml"));
}
测试类中添加查询所有blogs的方法
@Test
public void testFindBlogs(){
//1.创建SqlSession对象(相当于创建一个连接)
SqlSession session=factory.openSession();
//2.执行查询操作(selectList("命名空间"+"元素id"))
List<Object> list=session.selectList(
"com.jt.blog.BlogDao.findBlogs");
for(Object o:list){
System.out.println(o);
}
//3.释放资源(类似关闭连接)
session.close();
}
测试类中添加根据id执行查询方法
@Test
public void testFindBlogById(){
//1.创建session对象
SqlSession session=
factory.openSession();
//2.执行sql操作
String statement="com.jt.blog.BlogDao.findBlogById";
Map<String,Object> map=
session.selectOne(statement,1);
System.out.println(map);
//3.释放资源(关闭session对象)
session.close();
}
测试类中添加分页查询方法
@Test
public void testFindPageBlogs(){
//1.创建session对象
SqlSession session=
factory.openSession();
//2.执行sql操作?????
String statement="com.jt.blog.BlogDao.findPageBlogs";
Object parameter=new Object[]{0,4};
List<Map<?,?>> list=
session.selectList(statement, parameter);
for(Map<?,?>map:list){
System.out.println(map);
}
//3.释放资源(关闭session对象)
session.close();
};
测试类中添加插入数据的方法
@Test
public void testInsertObject(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
String statement="com.jt.blog.BlogDao.insertObject";
Object parameter=new Object[]{"te","te..."};
int rows=session.insert(statement, parameter);
System.out.println("insert.rows="+rows);
session.commit();
//3.关闭session
session.close();
}
测试类中添加修改方法
@Test
public void testUpdateObject(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
String statement="com.jt.blog.BlogDao.updateObject";
Object parameter=new Object[]{"taa","taa...",1};
int rows=session.update(statement, parameter);
System.out.println("update.rows="+rows);
session.commit();
//3.关闭session
session.close();
}
测试类中添加删除方法
@Test
public void testDeleteObject(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
String statement="com.jt.blog.BlogDao.deleteObject";
Object parameter=7;
int rows=session.delete(statement, parameter);
session.commit();
System.out.println("delete.rows="+rows);
//3.关闭session
session.close();
}
2. MyBatis 编程进阶
本小节中会从mybatis编程的另一个角度(例如基于接口方式等)实现对数据库中数据的操作.
2.1. 基本步骤
Step01: 创建maven桌面项目并添加依赖
Step02: 创建配置文件config.propertis(内容为数据库相关)
Step03: 创建mybatis核心配置文件mybatis-configs.xml文件
Step04: 配置Mybatis基础数据服务(properties,datasource,mapper)
Step05: 创建映射文件BlogMapper.xml
Step06: 创建实体类Blog(与表对应,可用于封装表中数据)
Step07: 创建BlogDao接口,并添加相关方法.
Step08: 配置BlogMapper映射文件,添加相关元素.
Step09: 基于BlogDao接口与映射文件实现CRUD操作
2.2. 编程实现
2.2.1. 创建Maven桌面项目
创建maven桌面项目并添加依赖
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- 添加mysql驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- 添加junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2.2.2. 创建config.properties文件
在src/main/resource目录下创建config.properties文件,此文件中定义
系统中的一些常用配置信息,例如访问数据库的相关信息,其内容如下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/cgb1711
username=root
password=root
2.2.3. 创建mybatis核心配置文件
在src/main/resources目录下创建mybatis-configs.xml文件,并配置数据源等相关信息,数据信息从config.properties文件读取.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置properties文件-->
<properties resource="config.properties"/>
<!-- 配置初始化环境(连接) -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
2.2.4. 创建Blog实体对象
创建Blog类实现与数据库中Blog表实现映射.
public class Blog {
private Integer id;
private String title;
private String content;
private Date createdTime;
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getTitle() {return title;}
public void setTitle(String title) {this.title = title;}
public String getContent() {return content; }
public void setContent(String content) {this.content = content;}
public Date getCreatedTime() {return createdTime; }
public void setCreatedTime(Date createdTime) {this.createdTime = createdTime; }
@Override
public String toString() {
return "Blog [id=" + id + ", title=" + title + ", content=" + content + ", createdTime=" + createdTime + "]";}
}
2.2.5. 创建BlogDao接口
创建数据访问接口,并添加相关方法
package com.jt.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.jt.entity.Blog;
public interface BlogDao {
/***
* 根据id进行对象查找
*/
Blog findBlogById(Integer id);
List<Blog> findPageBlogs(
@Param("offset")Integer offset,
@Param("pageSize")Integer pageSize);
int insertObject(Blog blog);
int updateObject(Blog blog);
int deleteObject(Integer id);
}
2.2.6. 创建BlogMapper映射文件
<?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.jt.dao.BlogDao">
</mapper>
BlogMapper文件中添加与BlogDao接口对应的映射元素
添加基于ID进行查询的元素
<select id="findBlogById"
parameterType="int"
resultType="blog">
select * from blog where id=#{id}
</select>
添加分页查询元素
<select id="findPageBlogs"
resultType="blog">
select *
from blog
limit #{offset},#{pageSize}
</select>
添加insert元素
<insert id="insertObject"
parameterType="blog">
insert into blog
(id,title,content,createdTime)
values
(null,#{title},#{content},now())
</insert>
添加更新元素
<update id="updateObject"
parameterType="blog">
update blog
set title=#{title},content=#{content}
where id=#{id}
</update>
添加删除元素
<delete id="deleteObject"
parameterType="int">
delete from blog where id=#{id}
</delete>
最后在mybatis-configs.xml中添加BlogMapper文件
2.2.7. 创建单元测试类执行测试
创建单元测试类,并添加相关方法实现基于Dao接口方式的数据库操作.
public class TestBlog01 {
private SqlSessionFactory factory
@Before
public void init()throws IOException{
factory=new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(
"mybatis-configs.xml"));
}
@Test
public void testFindBlogById(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
//2.1获取dao对象
BlogDao dao=
session.getMapper(BlogDao.class);
//2.2执行dao中方法
Blog blog=dao.findBlogById(1);
System.out.println(blog);
//3.关闭session
session.close();
}
@Test
public void testFindPageBlogs(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
//2.1获取dao对象
BlogDao dao=
session.getMapper(BlogDao.class);
//2.2执行dao中方法
List<Blog> list=dao.findPageBlogs(0, 2);
for(Blog b:list){
System.out.println(b);
}
//3.关闭session
session.close();
}
@Test
public void testInsertObject(){
//1.创建session
SqlSession session=factory.openSession();
//2.执行sql
//2.1获取dao对象
BlogDao dao=
session.getMapper(BlogDao.class);
//2.2执行dao中方法
Blog blog=new Blog();
blog.setTitle("te");
blog.setContent("te...");
dao.insertObject(blog);
session.commit();
//3.关闭session
session.close();
}
}
2.3. 业务增强
2.3.1. 动态排序策略
1. 如何按照用户指定业务对数据进行排序?
例如:
1) 博客系统
a) 按照创建时间对博客信息进行排序查询
b) 按照博客访问量对信息进行排序查询
2) 电商系统
a) 按照商品价格对商品信息排序
b) 按照商品销量对商品信息排序
c) 按照商品好评对商品信息排序
解决方案:
1) 写多个sql映射
2) 写一个sql 映射,然后动态传参,借助${}表达式获取参数
例如
接口中方法定义
List<Blog> findBlogs(
@Param("column") String column,
@Param("seq") String seq);
映射文件中的实现
<select id="findBlogs"
resultType="blog">
select *
from blog
order by ${column} ${seq}
</select>
拓展:
MyBatis中$与#有什么不同?
${}表达式主要用于获取配置文件数据,DAO接口中的参数信息,当$出现在映射文件的SQL语句中时创建的不是预编译的SQL,而是字符串的拼接,有可能会导致SQL注入问题.所以一般使用$接收dao参数时,这些参数一般是字段名,表名等,例如order by {column}.
#{}表达式主要用户获取DAO中的参数数据,在映射文件的SQL语句中出现#{}表达式,底层会创建预编译的SQL.性能会相对较好.
${}获取DAO参数数据时,参数必须使用@param注解进行修饰.
#{}获取DAO参数数据时,假如参数个数多于一个可有选择的使用@param.
2.3.2. ID应用策略
1.保存数据时获取数据在数据库对应的主键值?
例如
1) 订单系统?
保存订单信息时候,获取订单在数据库中的主键id值?
2) 权限系统
保存用户信息时,获取用户信息在数据库中的主键值?
解决方案: 当对象对应的表中的记录为自增长时,可以采用如下方案
<insert id="insertObject"
parameterType="blog"
useGeneratedKeys="true"
keyProperty="id">
insert into blog
(id,title,content,createdTime)
values
(null,#{title},#{content},now())
</insert>
其中:keyProperty属性用于指定参数中的id属性.
2. 当多线程并发的向表中写入数据时,假如id使用自增长可能存在线程安全问题?
例如:
1) 秒杀系统
2) 订票系统
3) ....
解决方案:可将自增长的id设置为随机数,当然有些数据库根本就不支持自增长,此时也可以选择随机数.
数据准备:创建Author表
create table Author(
id varchar(200) primary key,
username varchar(100) unique not null,
password varchar(300) not null,
email varchar(50) unique
)engine=InnoDB;
借助mybatis 应用向表中写入数据,主键值要求通过UUID生成.其映射文件参考
<insert id="insertObject"
parameterType="author">
<selectKey keyProperty="id"
resultType="string"
order="BEFORE">
select replace(uuid(),'-','')
</selectKey>
insert into author
(id,username,password,email)
values
(#{id},#{username},#{password},#{email})
</insert>
3. MyBatis 高级应用
3.1. 日志配置应用
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
1)SLF4J(日志框架标准,类似JDBC标准)
2)Apache Commons Logging
3)Log4j 2 (是log4j的升级版,配置文件升级为xml格式了)
4)Log4j(日志处理库,配置文件格式为.properties)
5)JDK logging
项目中mybatis通常会借助第三方日志库进行日志的处理,例如log4j.
配置步骤:(以log4j为例)
1. 添加log4j依赖(一代)
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.添加log4j.properties 配置(可以从其它项目中拷贝)
log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] %c - %m%n
log4j.logger.com.mybatis3=DEBUG
log4j.logger.com.jt=DEBUG
3. 设置mybatis的日志实现(mybatis-configs.xml)
<settings>
<setting name="logImpl" value="log4j"/>
</settings>
其中name属性的值为固定写法,value的值要依托于使用的日志处理库.
说明:课后了解常用的日志处理库.
3.2. 缓存配置应用
1. 缓存是什么? 内存中的一个对象(容器).
2. 缓存对象的作用?提高程序的性能(最主要的的是访问效率)
3. MyBatis 中缓存概述?
MyBatis 框架中提供了非常强大的缓存特性来提高查询性能,通常可分为一级缓存(SqlSession级别)和二级缓(SqlSessionFactory).
4. MyBatis 中的缓存应用.
MyBatis中一级缓存默认是开启的.不需要任何配置.例如:
@Test
public void testFindObjects(){
SqlSession session=factory.openSession();
AuthorDao dao=
session.getMapper(AuthorDao.class);
//==============
List<Author> list=
dao.findObjects();
session.close();
session=factory.openSession();
dao=session.getMapper(AuthorDao.class);
list=dao.findObjects();
//==============
session.close();
}
MyBatis 二级缓存默认是没有开启的,需要在映射文件中加上<Cache/>元素
MyBatis 二级缓存应用步骤:
Step01: 修改mybatis核心配置文件,添加缓存设置.
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
Step02: 在映射文件中配置Cache策略.
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
这个表示创建了一个 LRU缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。其中:
1) eviction 表示回收策略(例如LRU,FIFO等,默认为LRU)
2) flushInterval 表示刷新间隔
3) size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
4) readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
Step03: 使用二级缓存了.
@Test
public void testFindObjectById(){
SqlSession session=factory.openSession();
AuthorDao dao=session.getMapper(AuthorDao.class);
String id="c6dad9d622a311e88271408d5c85e6b9";
Author a1=dao.findObjectById(id);
System.out.println("a1="+a1);
session.close();
session=factory.openSession();
dao=session.getMapper(AuthorDao.class);
Author a2=dao.findObjectById(id);
a2.setUsername("tedu-10");
System.out.println("a2="+a2);
//System.out.println(a1==a2);
session.close();
session=factory.openSession();
dao=session.getMapper(AuthorDao.class);
Author a3=dao.findObjectById(id);
System.out.println("a3="+a3);
session.close();
}
readOnly说明:
readOnly的值为true时,缓存中保存的是堆内存中对象的引用.每次从缓存取数据都是获得的同一个对象.readOnly为false时,首先会将查询到的对象,拷贝到缓存一份(对象需要实现序列化接口),然后从缓存取数据每次都是执行对象的拷贝.
3.3. 高级映射应用
MyBatis中的高级映射一般要借助select元素中的resultMap属性进行实现,通过此属性配置实现一对一,一对多等关系映射的实现.
1. 如何实现one2one映射?
2. 如何实现one2many映射?
3. 如何实现many2many映射?
延伸:
1) 查询博客信息时,将作者信息也查询出来?(many2one)
2) 查询某个用户的同时,将其对应的博客列表信息也查询出来(one2many)
3) .....
1) 在blog表中添加一个字段,名字为authorId,类型为string.
2) 为blog表中的authorId字段赋值,其值为author表中某些记录的主键值.
3) 查询某个blog信息,并将其关联的author对象信息查询出来,
3.4. 延迟加载应用
对关联查询中的关联对象数据进行延迟加载(何时需要何时加载).以提高内存的使用效率.
具体如何配置?(参考官方文档)
3.5. 动态SQL应用
如何动态删除客户端选择的记录,用户有可能会选择一个记录,也可能会选择多个记录然后进行删除.那么在mybatis的映射文件中该如何配置,如何实现或者说解决这种动态需求问题?
了解:动态sql.