文章目录
1.框架概述
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
2.Mybatis快速入门
环境搭建
【1】创建 Mybatis 数据库,创建user表
数据准备:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `account`(`ID`,`UID`,`MONEY`) values (1,41,1000),(2,45,1000),(3,41,2000);
【2】创建maven工程,导入依赖。工程结构图:

- IuserDao接口为实现User表操作的dao接口。
- User表为实体类,其中属性尽量实现与数据库字段表名一致。
- resources下的dao.IUserDao.IUserDao.xml文件为IUserDao的映射配置文件。
- 把 lo4j.properties 文件复制到 resources 文件夹下。
- sqlMapConfig.xml为Mybatis主配置文件 。
- pom.xml为依赖包位置。
【3】文件pom.xml:
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
【4】创建实体类和dao接口
(类变量与数据库字段名保持一致)。
domain.user
public class User {
private int id;
private String username;
private Date birthday;
private String password;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", password='" + password + '\'' +
", address='" + address + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User() {
}
}
dao.IUseDao:
public interface IUserDao {
public List<User> findAll();
}
【5】创建mybatis的主配置文件
resources.SqlMapConfig:
<?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="mysql">
<!-- 配置mysql-->
<environment id="mysql">
<!-- 配置事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,也叫连接池-->
<dataSource type="POOLED">
<!-- 配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="dao/IUserDao.xml"/>
</mappers>
</configuration>
【6】创建mybatis的持久层接口的映射配置文件
- 创建位置:必须和持久层接口在相同的包中
- 名称:必须以持久层接口名称命名,拓展名为 .xml
resources.dao.IUserDao.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="dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="domain.User">
select * from user;
</select>
</mapper>
【7】Mybatis 测试类:
测试类中使用的模式
- SqlSessionFactory factory = builder.build(in);
构建者模式:把对象的创建细节隐藏,使用者直接调用方法即可拿到对象 - factory.openSession();
工厂模式:降低了类之间的依赖关系 - session.getMapper(IUserDao.class);
代理模式:不改变源码的基础上对已有的方法进行增强
public class MybatisTest {
public static void main(String[] args) throws Exception {
//1.读取配置文件
InputStream as = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(as);
//3.使用工厂生产SqlSession对象
SqlSession sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
//6.释放资源
sqlSession.close();
as.close();
}
}
环境搭建注意事项
第一个:创建IUserDao.xml和IUserDao.java时名称是为了和我们之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做: Mapper
所以: IUserDao和IUserMapper是一样的
第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时: com.itheima.dao它是三级结构
目录在创建时: com.itheima.dao是一级目录(所以多级目录要一级一级的创建)。
第三个: mybatis的映射配置文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select) , id属性的取值必须是dao接口的方法名
当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。
环境搭建步骤小结
第一步:创建 maven 工程
第二步:导入坐标
第三步:编写必要代码(实体类和持久层接口)
第四步:编写 SqlMapConfig.xml
第五步:编写映射配置文件
第六步:编写测试类
注解方式
- 删除
resources.dao.IUserDao.xml:映射配置文件 - 在接口中使用注解方式
IuserDao接口中添加方法
@Select("select * from user ;")
public List<User> findAll();
- 修改文件
resources.SqlMapConfig:
修改前:
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="dao/IUserDao.xml"/>
</mappers>
</configuration>
修改后:
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper class="dao.IUserDao"/>
</mappers>
</configuration>
3.Mybatis自定义流程
(手写源码)
4.Mybatis中CRUD操作
【1】接口中定义方法:
dao.IUserDao:
public interface IUserDao {
//查询所有方法
List<User> findAll();
//插入语句
void saveUser(User user);
//更新语句
void updateUser(User user);
//删除语句 根据id
void deleteUserByid(int id);
//查询语句 根据id
User findUserByid(int id);
//模糊查询语句
List<User> findUserByName(String name);
//查询总记录数
int findTotal();
//综合条件查询
List<User> findByVo(QueryVo vo);
}
QueryVo:
public class QueryVo {
private User user;
public QueryVo() {
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
【2】映射文件:
IUserDao.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="dao.IUserDao">
<!-- 配置查询-->
<select id="findAll" resultType="domain.User">
select *
from user;
</select>
<!-- 配置插入-->
<insert id="saveUser" parameterType="domain.User">
<!-- 配置获取保存数据之后的id -->
<selectKey keyProperty="id" resultType="Integer" keyColumn="id" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (username, birthday, sex, address)
values (#{username}, #{birthday}, #{sex}, #{address});
</insert>
<!-- 配置更新-->
<update id="updateUser" parameterType="domain.User">
update user
set username=#{username},
sex=#{sex},
address=#{address}
where id = #{id}
</update>
<!-- 配置删除 根据id-->
<delete id="deleteUserByid" parameterType="Integer">
delete
from user
where id = #{uid}
</delete>
<!-- 配置查询 根据id查询用户-->
<select id="findUserByid" parameterType="Integer" resultType="domain.User">
select *
from user
where id = #{uid}
</select>
<!-- 模糊查询语句-->
<select id="findUserByName" parameterType="String" resultType="domain.User">
select * from user where username like #{name}
<!-- select * from user where username like '%${value}%' -->
</select>
<!-- 查询总记录数-->
<select id="findTotal" resultType="Integer">
select count(id)
from user
</select>
<!-- 综合条件查询-->
<select id="findByVo" parameterType="domain.QueryVo" resultType="domain.User">
select * from user where username like #{user.username}
<!-- select * from user where username like '%${value}%' -->
</select>
</mapper>
【3】测试类:
mybaTest:
public class myba01Test {
private InputStream is;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void init() throws Exception {
//1.读取配置文件
is = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
factory = new SqlSessionFactoryBuilder().build(is);
//3.创建SqlSession对象
sqlSession = factory.openSession();
//4.获取反射对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void destroy() throws Exception {
//6.0提交事务
sqlSession.commit();
//6.释放资源
sqlSession.close();
is.close();
}
@Test
public void test1FindAll() {
//5.执行方法
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}
@Test //保存用户
public void test02SaveUser() {
User user = new User(0, "kobe", new Date(), "男", "Laker");
System.out.println("保存前:" + user);
userDao.saveUser(user);
System.out.println("保存后:" + user);
}
@Test //更新用户信息
public void test03UpdateUser() {
User user = new User(51, "Lahana", new Date(), "女", "Laker");
userDao.updateUser(user);
}
@Test //根据id删除用户
public void test04DeleteUserByid() {
userDao.deleteUserByid(57);
}
@Test //根据id查询用户
public void test05DFindUserByid() {
User userByid = userDao.findUserByid(48);
System.out.println(userByid);
}
@Test //模糊查询
public void test06FindUserByName() {
List<User> users = userDao.findUserByName("%王%");
// List<User> users = userDao.findUserByName("王");
for (User user : users) {
System.out.println(user);
}
}
@Test //根据总记录数
public void test07findTotal() {
int total = userDao.findTotal();
System.out.println(total);
}
@Test //综合条件查询
public void test08findByVo() {
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = userDao.findByVo(vo);
for (User user1 : users) {
System.out.println(user1);
}
}
}
5.resultType 和 parameterType
【1】parameterType参数见上
【2】resultType:
主要用处:数据库字段名和实体类名对应不上时,使用。
IUserDao.xml添加:
<!-- 建立 User 实体和数据库表的对应关系
type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->
<resultMap type="com.itheima.domain.User" id="userMap"> <id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
<! --id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
测试结果:
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
}
6.Mybatis实现传统Dao层开发
持久层dao接口:
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
/**
* 根据 id 查询
* @param userId
* @return
*/
User findById(Integer userId);
/**
* 保存用户
* @param user
* @return 影响数据库记录的行数
*/
int saveUser(User user);
}
持久层dao实现类:
public class UserDaoImpl implements IUserDao {
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
@Override
public List<User> findAll() {
SqlSession session = factory.openSession();
List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");
session.close();
return users;
}
public User findById(Integer userId) {
SqlSession session = factory.openSession();
User user = session.selectOne("com.itheima.dao.IUserDao.findById",userId);
session.close();
return user;
}
@Override
public int saveUser(User user) {
SqlSession session = factory.openSession();
int res = session.insert("com.itheima.dao.IUserDao.saveUser",user);
session.commit();
session.close();
return res;
}
}
持久层映射配置:
<?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.itheima.dao.IUserDao">
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
<!-- 根据 id 查询 -->
<select id="findById" resultType="com.itheima.domain.User"
parameterType="int">
select * from user where id = #{uid}
</select>
<!-- 保存用户
ognl 表达式:它是 apache 提供的一种表达式语言,在 struts2 中也有应用。
Object Graphic Navigation Language 对象图导航语言
它是按照一定的语法格式来获取数据的。
语法格式就是使用 #{对象.对象}的方式
#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并把值取
出来
-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
<!-- 配置保存时获取插入的 id -->
<selectKey keyColumn="id" keyProperty="id" resultType="int">
select last_insert_id();
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
测试类:
public class MybastisCRUDTest {
private InputStream in ;
private SqlSessionFactory factory;
private IUserDao userDao;
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
@Test
public void testFindOne() {
//6.执行操作
User user = userDao.findById(56);
System.out.println(user);
}
@Test
public void testSaveUser() throws Exception {
User user = new User();
user.setUsername("mybatis dao user");
}
//6.执行操作
int res = userDao.saveUser(user);
System.out.println(res);
System.out.println(user.getId());
}
7. sqlMapConfig配置文件
SqlMapConfig.xml 中配置的内容和顺序
-properties(属性)
--property
-settings(全局配置参数)
--setting
-typeAliases(类型别名)
--typeAliase
--package
-typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
--environment(环境子属性对象)
---transactionManager(事务管理)
---dataSource(数据源)
-mappers(映射器)
--mapper
--package
【1】properties属性
应用1:
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="root"/>
</properties>
数据库的datasource配置:
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
应用2:
首先,引入jdbcConfig.properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
<properties url="file:///D:/IDEA/Worksapce/mybatis/myba04Properties/src/main/resources/jdbcConfig.properties">
<!-- <properties resource="jdbcConfig.properties">-->
</properties>
数据库的datasource配置:
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
【2】 typeAliases(类型别名)
Mybatis 支持的默认别名,我们也可以采用自定义别名方式来开发。
自定义别名:
在 SqlMapConfig.xml 中配置:
<!-- 使用typeAliases配置别名,他只能配置domain中的类的别名 -->
<typeAliases>
<!-- 单个别名定义 type值实体类全限定类名,alias属性指定别名,当指定了别名就不区分大小写-->
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name="domain"/>
<package name="dao"/>
</typeAliases>
<!-- 使用typeAliases配置别名,他只能配置domain中的类的别名 -->
<typeAliases>
<!-- 单个别名定义 type值实体类全限定类名,alias属性指定别名,当指定了别名就不区分大小写-->
<typeAlias type="domain.User" alias="user"></typeAlias>
</typeAliases>
【3】mapper文件
<!-- 映射配置文件-->
<mappers>
<!-- 使用 mapper 接口类路径。
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。-->
<mapper resource="dao/IUserDao.xml"></mapper>
</mappers>
<!-- 映射配置文件-->
<mappers>
<!-- 注册指定包下的所有 mapper 接口
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
即接口文件名和xml文件名要一致-->
<!-- package 标签用于指定dao接口所在的包,当制定了之后就不用再写resource class -->
<package name="dao"></package>
</mappers>
8.连接池与事务控制
1、Mybatis 连接池的分类
Mybatis 将它自己的数据源 DataSource 分为三类:
UNPOOLED:不使用连接池的数据源
POOLED:使用连接池的数据源
JNDI:使用 JNDI 实现的数据源
相应地,MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource, PooledDataSource 类来表示 UNPOOLED、POOLED 类型的数据源。
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据 库连接,也就是我们所说的连接池技术)。
2、Mybatis 中数据源的配置
在 SqlMapConfig.xml 文件中,具体配置如下:
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatise"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
type=”POOLED”:MyBatis 会创建 PooledDataSource 实例
type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
3、Mybatis 中连接的获取过程分析
当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象 来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句 的时候。
只有当第 4句sqlSession.selectList(“findUserById”),才会触发MyBatis 在底层执行下面这个方 法来创建 java.sql.Connection 对象。
只有在要用到的时候,才去获取并打开连接,当我们用完了就再 立即将数据库连接归还到连接池中。
@Test
public void testSql() throws Exception {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("findUserById",41);
System.out.println(list.size());
}
4、Mybatis 的事务控制
Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC的 setAutoCommit()方法来设置事务提交方式的。
【1】手动提交事务
/**
* 测试插入数据
*/
@Test
public void insertTest() throws IOException {
User user = new User();
user.setAddress("袁州区");
user.setUsername("派大星");
user.setSex("男");
user.setBirthday(new Date());
//使用代理对象执行插入方法
userDao.insertUser(user);
//手动提交事务
sqlSession.commit();
}
使用 sqlSession.commit(); 手动提交事务,并且在控制台会有提交事务的打印信息,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中, 必须通过 sqlSession.commit()方法来执行提交操作。
【2】自动提交事务
使用 sqlSession.commit()提交事务,是因为在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit() 方法实现事务提交。
也可以设置自动提交事务,只需在创建 SqlSession 对象时设置参数为 true 即可:
@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建 SqlSessionFactory 的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用 SqlSessionFactory 生产 SqlSession 对象
sqlSession = factory.openSession(true);
//5.使用 SqlSession 创建 dao 接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
9.动态sql语句
1、 标签
【1】编写持久层接口
/**
* 根据条件查询
* @param user:查询的条件
* @return
*/
List<User> findByCondition(User user);
【2】持久层映射配置
<!--根据条件查询 if标签-->
<select id="findByCondition" resultType="com.LSTAR.domain.User" parameterType="com.LSTAR.domain.User">
select * from user where 1=1
<if test="username != null">
and username = #{username}
</if>
</select>
【3】测试类
/**
* 根据条件查询
*/
@Test
public void findByContion(){
User u = new User();
u.setUsername("派大星");
List<User> users = userDao.findByCondition(u);
for(User user : users){
System.out.println(user);
}
}
2、 < where> 标签
在<标签>中, where 1=1 表示的是永远为真,为了简化这个条件封装,可以采用 < where> 标签来进行简化
持久层映射配置
<select id="findByCondition" resultType="com.LSTAR.domain.User" parameterType="com.LSTAR.domain.User">
select * from user
<where>
<if test="username != null">
and username = #{username}
</if>
</where>
</select>
3、 标签
【1】在 QueryVo 中加入一个 List 集合用于封装参数
public class QueryVo implements Serializable {
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
【2】持久层映射配置
<foreach>:用于遍历集合,属性:
collection:代表要遍历的集合元素
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
<!--根据id集合查询用户-->
<select id="findByIds" parameterType="com.LSTAR.domain.QueryVo" resultType="com.LSTAR.domain.User">
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
【3】测试类
/**
* 根据queryVo中的id集合条件查询
*/
@Test
public void findByIdsTest() throws IOException {
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<Integer>();
list.add(41);
list.add(43);
list.add(49);
vo.setIds(list);
//使用代理对象执行查询方法
List<User> users = userDao.findByIds(vo);
for(User u : users) {
System.out.println(u);
}
}
10 多表查询
1、数据准备
实现多表查询,这里创建两张表,一个是用户表,一个是账户表,使用外键让用户表和账户表之间具备一对多的关系,并建立用户和账户的实体类
【1】创建 mybatise 数据库
创建好数据库后并创建 user 数据表和 account 数据表,并插入相关数据
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
【2】创建实体类
创建用户实体类和账户实体类:
public class User implements Serializable{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
【3】编写持久层接口
public interface IAccountDao {
/**
* 查询所有账户,同时还要获取到当前账户的所属用户信息
* @return
*/
List<Account> findAll();
}
public interface IUserDao {
/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List<User> findAll();
}
2、一对一查询(通过子类方式)
eg:现要实现查询所有账户信息,同时还要获取到当前账户的所属用户的用户名和地址信息
【1】定义 AccountUser 类
为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信 息,所以我们要在定义 AccountUser 类时可以继承 User 类。
public class AccountUser extends Account {
private String username;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString()+" AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
【2】编写持久层接口
```java
/**
* 查询所有账户,并且带有用户名称和地址信息
* @return
*/
List<AccountUser> findAllAccount();
【3】持久层映射配置
```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.itheima.dao.IUserDao">
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select a.*,u.username,u.address from account a,user u where a.uid =u.id;
</select>
</mapper>
【4】测试类
/**
* 测试查询所有账户,同时包含用户名称和地址
*/
@Test
public void testFindAllAccountUser(){
List<AccountUser> aus = accountDao.findAllAccount();
for(AccountUser au : aus){
System.out.println(au);
}
}
3、一对一查询(通过建立实体类方式)
通过建立实体类的方式,使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果,所以可以在 Account 类中加入 User 类的对象作为 Account 类的一个属性。
【1】修改 Account 类
在 Account 类中添加以下属性:
//从表实体应该包含一个主表实体的对象引用
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
【2】修改 AccountDao 接口中的方法
将返回对象改为 Account 类型,因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息
/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<Account> findAll();
【3】重新定义 AccountDao.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.itheima.dao.IAccountDao">
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容-->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
</select>
<!--查询所有账户同时包含用户名和地址信息-->
<select id="findAllAccount" resultType="accountuser">
select a.*,u.username,u.address from account a , user u where u.id = a.uid;
</select>
</mapper>
【4】测试类
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
for(Account account : accounts){
System.out.println("--------每个account的信息------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
4、一对多查询
要实现查询所有用户信息以及用户关联的账户信息,即一对多查询,在查询过程中,如果用户没有账户信息,此时也要将用户信息查询出来,可以使用左外键连接查询。
【1】User类中添加List
//一对多关系映射:主表实体应该包含从表实体的集合引用
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
【2】编写持久层接口
/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List<User> findAll();
【3】持久层映射配置
collection标签:部分定义了用户关联的账户信息,表示关联查询结果集,属性有:
property=“accList”:关联查询的结果集存储在 User 对象上哪个属性
ofType=“account”:指定关联查询的结果集中的对象类型即 List 中的对象类型,此处可以使用别名,也可以使用全类名
<?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.itheima.dao.IUserDao">
<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="account">
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
</mapper>
【4】测试类
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for(User user : users){
System.out.println("-----每个用户的信息------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
11.延迟加载
延迟加载参考
从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
【1】associatio
需求:查询账户信息同时查询用户信息。(多对一)
【2】collection
同样我们也可以在一对多关系配置的结点中配置延迟加载策略。
结点中也有 select 属性,column 属性。
需求:
完成加载用户对象时,查询该用户所拥有的账户信息。(多对多)
12.缓存
- 一级缓存
默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库
- 一级缓存在下面情况会被清除
在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存。
对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
在全局配置文件中设置 ,这样会使一级缓存失效,二级缓存不受影响
- 二级缓存
默认关闭,可通过全局配置文件中的开启二级缓存总开关,然后在某个具体的mapper.xml中增加,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中
13.注解开发
1.mybatis 的常用注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
2.使用 Mybatis 注解实现基本 CRUD
【1】编写实体类:
/**
*
* <p>Title: User</p>
* <p>Description: 用户的实体类</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId; }
public void setUserId(Integer userId) {
this.userId = userId; }
public String getUserName() {
return userName; }
public void setUserName(String userName) {
this.userName = userName; }
public Date getUserBirthday() {
return userBirthday; }
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday; }
public String getUserSex() {
return userSex; }
public void setUserSex(String userSex) {
this.userSex = userSex; }
public String getUserAddress() {
return userAddress; }
public void setUserAddress(String userAddress) {
this.userAddress = userAddress; }
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", userBirthday="
+ userBirthday + ", userSex="
+ userSex + ", userAddress=" + userAddress + "]";
}
}
/* 注意:
此处我们故意和数据库表的列名不一致。*/
【2】注解方式开发持久层接口:
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
List<User> findAll();
* 根据 id 查询一个用户
* @param userId
* @return
*/
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
/**
* 保存操作
* @param user
* @return
*/
@Insert("insert into
user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address}
)")
@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before =
false, statement = { "select last_insert_id()" })
int saveUser(User user);
【3】sqlMapConfig改变
<!-- 配置映射信息 -->
<mappers>
<!-- 配置 dao 接口的位置,它有两种方式
第一种:使用 mapper 标签配置 class 属性
第二种:使用 package 标签,直接指定 dao 接口所在的包
-->
<package name="com.itheima.dao"/>
</mappers>
【4】测试方法:
/**
*
* <p>Title: MybatisAnnotationCRUDTest</p>
* <p>Description: mybatis 的注解 crud 测试</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class MybatisAnnotationCRUDTest {
/**
* 测试查询所有
*/
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
}
/**
* 测试查询一个
*/
@Test
public void testFindById() {
User user = userDao.findById(41);
System.out.println(user);
}
/**
* 测试保存
*/
@Test
public void testSave() {
User user = new User();
user.setUserName("mybatis annotation");
user.setUserSex("男");
user.setUserAddress("北京市顺义区");
user.setUserBirthday(new Date());
int res = userDao.saveUser(user);
System.out.println("影响数据库记录的行数:"+res);
System.out.println("插入的主键值:"+user.getUserId());
}
}
本文详细介绍了MyBatis框架的基本概念、快速入门指南、自定义流程、CRUD操作等内容,并探讨了resultType和parameterType的区别,展示了如何使用MyBatis实现传统DAO层开发。

218

被折叠的 条评论
为什么被折叠?



