文章目录
mybatis概述
mybatis简介
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.
mybatis历史
原是apache的一个开源项目iBatis, 2010年6月这个项目由apache software foundation 迁移到了google code,随着开发团队转投Google Code旗下,ibatis3.x正式更名为Mybatis ,代码于2013年11月迁移到Github(下载地址见后)。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
为什么要使用mybatis。
MyBatis是一个半自动化的持久化层框架。
jdbc编程—当我们使用jdbc持久化的时候,sql语句被硬编码到java代码中。这样耦合度太高。代码不易于维护。在实际项目开发中会经常添加sql或者修改sql,这样我们就只能到java代码中去修改。
Hibernate和JPA
长而又难又复杂SQL,对于Hibernate而言处理也不容易
内部自动生产的SQL,不容易做特殊优化。
基于全映射的全自动框架,javaBean存在大量字段时无法只映射部分字段。导致数据库性能下降。
对开发人员而言,核心sql还是需要自己优化
sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录。成为业务代码+底层数据库的媒介
mybatis的Hello 示例程序
- 创建一个数据库和一个单表
drop database if exists mybatis;
create database mybatis;
use mybatis;
##############################################################################
################################### 单表 ######################################
##############################################################################
## 创建单表
create table t_user(
`id` int primary key auto_increment,
`last_name` varchar(50),
`sex` int
);
insert into t_user(`last_name`,`sex`) values('wzg168',1);
select * from t_user;
- Mybatis程序示例:
1 先编写数据库表对应的User对象
public class User {
private Integer id;
private String lastName;
private Integer sex;
2 编写mybatis的核心配置文件 : mybatis-config.xml ( 放在src目录下 )
<?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是配置数据库连接属性 -->
<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://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--
我们说,mybatis是把sql语句配置到xml配置文件中
告诉mbyatis到哪里去加载sql语句的配置文件
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers> -->
</configuration>
测试的代码:
package com.atguigu.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisHelloTest {
@Test
public void test() throws IOException {
// 从 XML 中构建 SqlSessionFactory
// 读取mybatis的核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过sqlSessionFactoryBuilder创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
System.out.println(sqlSessionFactory);
}
}
3 编写Mapper接口 ( DAO接口一样 )
JavaBean名 Mapper接口命名习惯( 不需要自己实现 ) Dao命名 ( 需要自己实现 )
User UserMapper UserDao
Book BookMapper BookDao
在 mybatis中我们需要为Mapper接口编写mapper接口的方法中执行的sql语句的配置文件.
JavaBean名 Mapper接口名 sql配置文件名
User UserMapper UserMapper.xml
Book BookMapper BoookMapper.xml
Mapper.xml和Mapper接口一般都是放在同一个包下
Mapper接口:
public interface UserMapper {
// mybatis的hello world ==>>> 要求我们查询数据库表t_user中id为1的记录
public User queryUserById(Integer id);
}
Mapper.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名称空间属性值
它的属性值必须是Mapper接口的全类名
-->
<mapper namespace="com.atguigu.mapper.UserMapper">
<!--
public User queryUserById(Integer id);
select标签表示配置一个select查询语句
id 属性值必须是方法名
parameterType 属性配置方法的参数类型 int 表示 java.lang.Integer类型
resultType 属性设置方法的返回值类型
在mybatis中占位符要写成为: #{}
-->
<select id="queryUserById" parameterType="int" resultType="com.atguigu.pojo.User">
select `id`,`last_name` lastName, `sex` from t_user where id = #{id}
</select>
</mapper>
修改Mybatis-config.xml核心配置文件,加载sql的配置文件:
<?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是配置数据库连接属性 -->
<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://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--
我们说,mybatis是把sql语句配置到xml配置文件中
告诉mbyatis到哪里去加载sql语句的配置文件
-->
<mappers>
<!--说,去加载UserMapper.xml配置文件 -->
<mapper resource="com/atguigu/mapper/UserMapper.xml"/>
</mappers>
</configuration>
测试代码:
@Test
public void test2() throws IOException {
// 创建SqlSessionFactory对象
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// SqlSession相当于Jdbc中的Connection连接对象
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper的实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
} finally {
session.close();
}
}
给mybatis添加日记操作:
先添加日记的jar包
junit_4.12.jar
log4j-1.2.17.jar
mybatis-3.5.1.jar
mysql-connector-java-5.1.7-bin.jar
org.hamcrest.core_1.3.0.jar
在src目录下,配置日记功能的配置文件. log4j.properties
# 全局日志配置
log4j.rootLogger=DEBUG, stdout
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- Mapper接口的CRUD实现
Mapper接口:
public interface UserMapper {
// mybatis的hello world ==>>> 要求我们查询数据库表t_user中id为1的记录
public User queryUserById(Integer id);
public List<User> queryUsers();
public int saveUser(User user);
public int deleteUserById(Integer id);
public int updateUserById(User user);
}
Mapper.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名称空间属性值
它的属性值必须是Mapper接口的全类名
-->
<mapper namespace="com.atguigu.mapper.UserMapper">
<!--
public User queryUserById(Integer id);
select标签表示配置一个select查询语句
id 属性值必须是方法名
parameterType 属性配置方法的参数类型 int 表示 java.lang.Integer类型
resultType 属性设置方法的返回值类型
在mybatis中占位符要写成为: #{}
-->
<select id="queryUserById" resultType="com.atguigu.pojo.User">
select `id`,`last_name` lastName, `sex` from t_user where id = #{id}
</select>
<!-- public List<User> queryUsers();
resultType 属性设置查询回来的结果每一行转换成为什么具体的类型
-->
<select id="queryUsers" resultType="com.atguigu.pojo.User">
select `id`,`last_name` lastName, `sex` from t_user
</select>
<!-- public int saveUser(User user);
insert语句配insert标签
update语句配update标签
delete语句配delete标签
select语句配select标签
-->
<insert id="saveUser" parameterType="com.atguigu.pojo.User">
insert into t_user( `last_name`,`sex` ) values(#{lastName},#{sex})
</insert>
<!-- public int deleteUserById(Integer id);-->
<delete id="deleteUserById" >
delete from t_user where id = #{id}
</delete>
<!-- public int updateUserById(User user);-->
<update id="updateUserById" parameterType="com.atguigu.pojo.User">
update
t_user
set
`last_name` = #{lastName} ,
`sex` = #{sex}
where
id = #{id}
</update>
</mapper>
Mapper接口测试
package com.atguigu.test;
import com.atguigu.mapper.UserMapper;
import com.atguigu.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
public class UserMapperTest {
static SqlSessionFactory sqlSessionFactory;
/**
* @BeforeClass注解要标识在static的方法上,它会在测试方法执行之前执行,做一些初始化工作. <br/>
* @BeforeClass 属于junit4测试类
*/
@BeforeClass
public static void init() throws IOException {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml") );
}
@Test
public void queryUserById() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
} finally {
sqlSession.close();
}
}
@Test
public void queryUsers() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.queryUsers().forEach(System.out::println);
} finally {
session.close();
}
}
@Test
public void saveUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.saveUser(new User(null, "ccccccc", 1));
System.out.println(i);
session.commit();//手动提交事务,否则事务就默认回滚
} finally {
session.close();
}
}
@Test
public void deleteUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.deleteUserById(5);
// 写操作,都需要手动提交事务
session.commit();
} finally {
session.close();
}
}
@Test
public void updateUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.updateUserById(new User(4,"oomm", 0));
session.commit();
} finally {
session.close();
}
}
}
使用Mapper接口开发需要注意的四个点:
1 Mapper.xml配置文件的名称空间值必须是Mapper接口的全类名
2 Mapper.xml配置文件中的id值必须是Mapper接口的方法名
3 Mapper.xml配置文件文件中参数的类型parameterType必须和Mapper接口的参数类型一致( parameterType参数可选 )
4 Mapper.xml配置文件中返回值类型resultType必须和Mapper接口的返回值类型一致
插入记录并返回主键
一般在插入数据库数据的时候,经常需要把当前插入的生成的id主键值返回.
<!-- public int saveUser(User user);
insert语句配insert标签
update语句配update标签
delete语句配delete标签
select语句配select标签
useGeneratedKeys="true" 使用数据库生成的主键值
keyProperty="id" 将返回的主键值,注入到哪个属性中
-->
<insert id="saveUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.atguigu.pojo.User">
insert into t_user( `last_name`,`sex` ) values(#{lastName},#{sex})
</insert>
selectKey 标签的使用
selectKey标签可以给当前执行的sql语句再配置另一个查询功能的sql.
<insert id="saveUser" parameterType="com.atguigu.pojo.User">
<!--
selectKey标签可以配置一个查询语句
order设置selectkey中的查询语句的执行顺序
AFTER 后执行
Before 先执行
keyProperty="id" 将返回的主键值,注入到哪个属性中
resultType="int" 表示返回的数据是什么类型
-->
<selectKey order="AFTER" keyProperty="id" resultType="int">
select last_insert_id()
</selectKey>
insert into t_user( `last_name`,`sex` ) values(#{lastName},#{sex})
</insert>
注解@KeyMap的使用。
@KeyMap可以把查询回来的多条记录封装为Map.
map的键是: 就是我们指定的唯一值的列 ( 一般使用主键列 )
map的值是: 每行记录转换的对象
Mapper接口:
/**
* 1 将查询回来每一行记录都封装为USer对象. <br>
* 2 按照指定的列做为key保存查询到的数据,封装为Map <br/>
* @return
*/
@MapKey("id")
public Map<Integer,User>queryUserForMap();
Mapper.xml配置文件:
<!-- /**
* 1 将查询回来每一行记录都封装为USer对象. <br>
* 2 按照指定的列做为key保存查询到的数据,封装为Map <br/>
* @return
*/
@MapKey("id")
public Map<Integer,User>queryUserForMap();
-->
<select id="queryUserForMap" resultType="com.atguigu.pojo.User">
select `id`,`last_name`, `sex` from t_user
</select>
测试代码:
@Test
public void queryUserForMap() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
Map<Integer, User> map = userMapper.queryUserForMap();
System.out.println( map.getClass() );
for (Map.Entry<Integer, User>entry : map.entrySet()) {
System.out.println( entry.getKey() + " = " + entry.getValue());
}
} finally {
session.close();
}
}
mybatis的核心配置之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标签可以配置键值对
这里的键值对可以供数据库环境使用.
resource属性用来加载外部的jdbc.properties属性信息
-->
<properties resource="jdbc.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</properties>
<!-- environments是配置数据库连接属性 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--修改数据库的连接属性-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--
我们说,mybatis是把sql语句配置到xml配置文件中
告诉mbyatis到哪里去加载sql语句的配置文件
-->
<mappers>
<!--说,去加载UserMapper.xml配置文件 -->
<mapper resource="com/atguigu/mapper/UserMapper.xml"/>
</mappers>
</configuration>
mybatis的核心配置之settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。
mybatis的核心配置之typeAliases
<!--配置别名-->
<typeAliases>
<!--typeAlias配置一个别名
type是你的具体类型的全类名
alias 是别名
<typeAlias type="com.atguigu.pojo.User" alias="user" />
-->
<!-- 通过配置包名的方式配置所有JavaBean的别名
别名到底是啥,怎么用
默认别名,就是类名,但一般用类名首字母小写居多
-->
<package name="com.atguigu.pojo"/>
</typeAliases>
mybatis的核心配置之typeHandlers
mybatis的核心配置之environments
environments 标签用于配置数据库环境,
可以配置多个
SqlSessionFactory是单例的. 一个数据库对应一个SqlSessionFactory对象
environment 标签说明
environment 可以配置一个数据库环境
transactionManager 标签说明
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
• JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
• MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
dataSource 标签说明
type 属性的值有三种: UNPOOLED 、 POOLED 、 JNDI。自定义(实现DataSourceFactory接口)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
• 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形
mybatis的核心配置之databaseIdProvider
它可以提供多数据库的支持.
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver" />
<property name="MySQL" value="mysql" />
<property name="DB2" value="db2" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
databaseId测试
<!-- public List<User> queryUsers();
resultType 属性设置查询回来的结果每一笔转换成为什么具体的类型
而不带有 databaseId 属性的它可以用于执行任何数据库
-->
<select id="queryUsers" resultType="user">
select `id`,`last_name` lastName, `sex` from t_user
</select>
<!--
带有 databaseId 属性的是只是匹配了当前数据库就会执行的sql
-->
<select id="queryUsers" resultType="user" databaseId="oracle">
select `id`,`last_name` lastName, `sex` from t_user where 1 = 1
</select>
mybatis的核心配置之Mapper ( 使用最多 )
<!--
我们说,mybatis是把sql语句配置到xml配置文件中
告诉mbyatis到哪里去加载sql语句的配置文件
-->
<mappers>
<!--说,去加载UserMapper.xml配置文件 -->
<!--
是指从ClassPath类路径下开始搜索配置文件
<mapper resource="com/atguigu/mapper/UserMapper.xml"/> -->
<!--
mapper表示配置搜索sql的配置文件
class 表示搜索指定接口对应的配置文件
注意事项:
1 Mapper接口和Mapper.xml配置文件,必须在同一个包下
2 Mapper接口文件名和Mapper.xml文件名要一致
<mapper class="com.atguigu.mapper.UserMapper" />
-->
<!--配置按照指定的包名去搜索所有Mapper.xml配置文件-->
<package name="com.atguigu.mapper"/>
</mappers>
注解配置sql语句
编写一个模块,使用注解去配置方法的sql语句
UserMapper接口:
public interface UserMapper {
/**
* @Insert 配置 insert的sql语句 <br/>
* @SelectKey 配置一个查询语句( 主要用于配置一个Select语句,用于查询主键 ) <br>
* statement 配置你的sql语句 <br>
* before 配置是否是先执行selectKey中语句 <br/>
* keyProperty 配置查询回来的Key主键给哪个属性赋值 <br/>
* resultType 属性配置返回的主键数据类型
* @param user
* @return
*/
@SelectKey(statement = "select last_insert_id()",before = false, keyProperty = "id",resultType = Integer.class)
@Insert("insert into t_user(`last_name`,`sex`) values(#{lastName},#{sex})")
public int saveUser(User user);
/**
* @Update 配置 update语句
* @param user
* @return
*/
@Update("update t_user set `last_name` = #{lastName} , `sex` = #{sex} where id = #{id}")
public int updateUserById(User user);
/**
* @Delete 注解 配置 delete 语句
* @param id
* @return
*/
@Delete("delete from t_user where id = #{id}")
public int deleteUserById(Integer id);
/**
*@Select 注解配置 select语句
* @param id
* @return
*/
@Select("select `id`, `last_name` lastName,`sex` from t_user where id = #{id}")
public User queryUserById(Integer id);
/**
* @Select 注解配置 select语句
* @return
**/
@Select("select `id`, `last_name` lastName,`sex` from t_user")
public List<User> queryUsers();
}
测试的代码:
public class UserMapperTest {
/**
* sqlSessionFactory是单例的. 一个数据库只有一个SqlSessionFactory对象实例
*/
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() throws Exception{
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void saveUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(null,"ddee" , 1);
System.out.println(userMapper.saveUser(user));
System.out.println(user);
session.commit();
} finally {
session.close();
}
}
@Test
public void updateUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(7,"bbj" , 0);
userMapper.updateUserById(user);
session.commit();
} finally {
session.close();
}
}
@Test
public void deleteUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(7,"bbj" , 0);
userMapper.deleteUserById(8);
session.commit();
} finally {
session.close();
}
}
@Test
public void queryUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.queryUserById(9));
} finally {
session.close();
}
}
@Test
public void queryUsers() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取UserMapper实现类
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.queryUsers().forEach(System.out::println);
} finally {
session.close();
}
}
}
数据库中的自增是不回滚的。
注解方式和xml方式可以共用,但不能同时写。一般来说,简单的SQL语句使用注解方式,复杂的SQL语句使用xml方式。
mybatis的参数传递
参数传递是指如何将mapper接口方法中的参数传递给sql语句的占位符. 简单点说,就是 #{} 里应该写啥
一个普通数据类型
Mapper接口:
public interface UserMapper {
public User queryUserById(Integer id) ;
}
Mapper.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">
<!--
namespace名称空间的值要改为对应的Mapper接口的全类名
-->
<mapper namespace="com.atguigu.mapper.UserMapper">
<!-- public User queryUserById(Integer id);
当Mapper接口参数是一个普通类型的时候,#{}中可以写任意的内容,
只是推荐写成为参数名.为了更好的可读性
-->
<select id="queryUserById" resultType="com.atguigu.pojo.User">
select `id`,`last_name` lastName,`sex` from t_user where id = #{id}
</select>
</mapper>
测试代码:
public class UserMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() throws Exception{
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void queryUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
} finally {
session.close();
}
}
}
多个普通数据类型
Mapper接口:
/**
* 根据给定的性别和名称查询用户信息 <br/>
*
* @Param("name") String name 表示将第一个参数的参数名设置为name.
*
* @return
*/
public List<User> queryUsersBySexOrName(String name, Integer sex);
Mapper.xml配置文件:
<!-- /**
* 根据给定的性别和名称查询用户信息
* @return
*/
public List<User> queryUsersBySexOrName(String name,Integer sex);
resultType属性是指定查询回来的每一行数据转换的具体的类型,
在传递的参数是多个普通类型的情况下.有两种方案可以传递参数的值到占位符中.
第一种情况是: (mybatis3.5支持)(mybatis3.4是用0,1来表示的)
arg0 表示第一个参数
arg1 表示第二个参数
......以此类推
argN 表示第N+1个参数
第二种情况是:(推荐使用)
param1 表示第一个参数
param2 表示第二个参数
......以此类推
paramN 表示第n个参数
-->
<select id="queryUsersBySexOrName" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
`last_name` = #{param1} or `sex` = #{param2}
</select>
测试的代码:
@Test
public void queryUsersBySexOrName(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.queryUsersBySexOrName("ccccccc", 1).forEach(System.out::println);
} finally {
session.close();
}
}
@Param注解命名参数
Mapper接口:
/**
* 根据给定的性别和名称查询用户信息 <br/>
*
* @Param("name") String name 表示将第一个参数的参数名设置为name.
*
* @return
*/
public List<User> queryUsersBySexOrName(@Param("name") String name, @Param("sex") Integer sex);
Mapper.xml配置文件:
<!--
public List<User> queryUsersBySexOrName(@Param("name") String name, @Param("sex") Integer sex);
如果我们在参数中使用了注解@Param("name") String name,则参数name的值可以通过注解指定的名称来进行传递
也就是如下:
@Param("name") String name =====>>>>> #{ name }
@Param("sex") Integer sex =====>>>>> #{ sex }
....... 以此类推
@Param("xxxx") Integer sex =====>>>>> #{ xxxx }
-->
<select id="queryUsersBySexOrName" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
`last_name` = #{name} or `sex` = #{sex}
</select>
传递一个Map对象作为参数(使用极少)
Mapper接口
/**
* 按照map中给定的name和sex来进行查询用户信息
* @param paramMap
* @return
*/
public List<User> queryUsersByMap(Map<String,Object> paramMap);
Mapper.xml配置文件:
<!-- /**
* 按照map中给定的name和sex来进行查询用户信息
* @param paramMap
* @return
*/
public List<User> queryUsersByMap(Map<String,Object> paramMap);
如果传递的参数是Map类型,则在#{}中需要写上map的key值表示传递相应key的值到sql的占位符中.
示例代码:
UserMapper userMapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("name","ccccccc");
map.put("sex",1);
userMapper.queryUsersByMap(map).forEach(System.out::println);
使用如下:
Map<String,Object> map = new HashMap<>();
map.put("name","ccccccc"); ====>>>> #{ name }
map.put("sex",1); ====>>>> #{ sex }
-->
<select id="queryUsersByMap" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
`last_name` = #{name} or `sex` = #{sex}
</select>
测试的代码
@Test
public void queryUsersByMap(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("name","ccccccc");
map.put("sex",1);
userMapper.queryUsersByMap(map).forEach(System.out::println);
} finally {
session.close();
}
}
一个Pojo数据类型( pojo就是JavaBean )
Mapper接口:
/**
* 把 User 保存到数据库
* @param user
* @return
*/
public int saveUser(User user);
Mapper.xml配置文件:
<!-- /**
* 把 User 保存到数据库
* @param user
* @return
*/
public int saveUser(User user);
如果参数是一个javaBean的时候,只需要在#{}占位符中写上对应数据库列的javaBean的属性名即可.
public class User {
private Integer id;
private String lastName; ====>>> #{lastName} 表示传递 lastName 属性值给相应的占位符
private Integer sex; ====>>> #{sex} 表示传递 sex 属性值给相应的占位符
-->
<insert id="saveUser" parameterType="com.atguigu.pojo.User">
insert
into t_user(`last_name`,`sex`)
values (#{lastName},#{sex})
</insert>
测试代码:
@Test
public void saveUser(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.saveUser(new User(null,"xxx", 1));
session.commit();
} finally {
session.close();
}
}
模糊查询
需求:现在要根据用户名查询用户对象。 也就是希望查询如下: select * from t_user where user_name like ‘%张%’
Mapper接口:
/**
* 根据给定的name值做like模糊查询用户信息
* @param name
* @return
*/
public List<User> queryUsersByNameLike(String name);
Mapper.xml配置文件:
<!-- /**
* 根据给定的name值做like模糊查询用户信息
* @param name
* @return
*/
public List<User> queryUsersByNameLike(String name);-->
<select id="queryUsersByNameLike" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
`last_name` like #{name}
</select>
测试的代码:
@Test
public void queryUsersByNameLike() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
String name = "%xx%";
userMapper.queryUsersByNameLike(name).forEach(System.out::println);
} finally {
session.close();
}
}
${}的使用
在进行order by 排序的时候,不能使用#{},因为会带有双引号,#{}会考虑类型。只能用${},能使用#{}就使用#{}。
#{} 是占位符 , 所以不会有sql注入的问题
${} 是把参数内容原样输出 , 然后和原sql语句做字符串拼接操作. 会有sql注入的安全性问题
#{} 在一个参数的时候,可以任意写内容
${} 里只能写value, 或者使用@Param注解
#{} 一般用于给参数注入值
${} 一般用于表名
MySQL的字符串拼接,concat函数实现。
在Mapper.xml中的使用:
<!-- /**
* 根据给定的name值做like模糊查询用户信息
* @param name
* @return
*/
public List<User> queryUsersByNameLike(String name);-->
<select id="queryUsersByNameLike" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
`last_name` like concat('%',#{name},'%');
</select>
自定义结果集 resultMap标签
resultMap标签可以对查询回来的结果集进行手动映射结果.
就是把查询回来的resultSet表中的数据如何指定哪个列赋值给JavaBean的哪个属性.
resultType 属性它可以指定一个JavaBean的具体类型.让查询回来的resultSet结果集自动映射上列名和属性名相同的属性.
resultMap的作用。
resultMap标签又叫自定义结果集标签.
resultMap标签经常使用在复杂的Bean对象上.并不会使用在简单的Bean对象上.
简单的Bean对象,是指JavaBean的属性中都是基本的数据类型.
复杂的Bean对象,是指属性的类型又是Bean对象 . 或者是集合的情况.
一般简单的Bean对象 ,都使用resultType属性使用自动映射结果集就可以实现功能效果了.
而复杂的JavaBean才会使用ResultMap标签来进行自定义结果集.
创建一对一数据库表
## 一对一数据表
## 创建锁表
create table t_lock(
`id` int primary key auto_increment,
`name` varchar(50)
);
## 创建钥匙表
create table t_key(
`id` int primary key auto_increment,
`name` varchar(50),
`lock_id` int ,
foreign key(`lock_id`) references t_lock(`id`)
);
## 插入初始化数据
insert into t_lock(`name`) values('阿里巴巴');
insert into t_lock(`name`) values('华为');
insert into t_lock(`name`) values('联想');
insert into t_key(`name`,`lock_id`) values('马云',1);
insert into t_key(`name`,`lock_id`) values('任正非',2);
insert into t_key(`name`,`lock_id`) values('柳传志',3);
创建实体对象
public class Key {
private Integer id;
private String name;
private Lock lock;
public class Lock {
private Integer id;
private String name;
一对一级联属性使用
KeyMapper接口:
public interface KeyMapper {
/**
* 根据指定key的id值,一次性查询出key所有信息,以及key中lock的信息.
* @param id
* @return
*/
public Key queryKeyById(Integer id);
}
Mapper.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">
<!--
namespace名称空间的值要改为对应的Mapper接口的全类名
-->
<mapper namespace="com.atguigu.mapper.KeyMapper">
<!--
resultMap自定义结果集
id 唯一标识
type 每一行转换的具体对象类型
-->
<resultMap id="queryKeyByIdResultMap" type="com.atguigu.pojo.Key">
<!--主键列映射-->
<id column="id" property="id" />
<!--非主键列映射-->
<result column="name" property="name" />
<!--
级联属性映射
级联 ===>>>> 一级一级关联
-->
<result column="lock_id" property="lock.id" />
<result column="lock_name" property="lock.name" />
</resultMap>
<!-- /**
* 根据指定key的id值,一次性查询出key所有信息,以及key中lock的信息.
* @param id
* @return
*/
public Key queryKeyById(Integer id);-->
<select id="queryKeyById" resultMap="queryKeyByIdResultMap">
select
t_key.* ,
t_lock.name lock_name
from
t_key left join t_lock
on
t_key.lock_id = t_lock.id
where
t_key.id = #{id}
</select>
</mapper>
测试的代码:
public class KeyMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() throws Exception{
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void queryKeyById() {
SqlSession session = sqlSessionFactory.openSession();
try {
KeyMapper keyMapper = session.getMapper(KeyMapper.class);
System.out.println(keyMapper.queryKeyById(1));
} finally {
session.close();
}
}
}
association 标签 嵌套结果集映射配置
association 标签可以给Bean的子对象进行赋值映射操作.
<!--
resultMap自定义结果集
id 唯一标识
type 每一行转换的具体对象类型
-->
<resultMap id="queryKeyByIdResultMap" type="com.atguigu.pojo.Key">
<!--主键列映射-->
<id column="id" property="id" />
<!--非主键列映射-->
<result column="name" property="name" />
<!--association标签可以配置子对象
property 属性表示子对象名称
javaType 表示子对象的全类名
-->
<association property="lock" javaType="com.atguigu.pojo.Lock">
<result column="lock_id" property="id" />
<result column="lock_name" property="name" />
</association>
</resultMap>
<!-- /**
* 根据指定key的id值,一次性查询出key所有信息,以及key中lock的信息.
* @param id
* @return
*/
public Key queryKeyById(Integer id);-->
<select id="queryKeyById" resultMap="queryKeyByIdResultMap">
select
t_key.* ,
t_lock.name lock_name
from
t_key left join t_lock
on
t_key.lock_id = t_lock.id
where
t_key.id = #{id}
</select>
association 定义分步查询
association 标签还可以通过调用一个查询得到子对象.
LockMapper接口:
public interface LockMapper {
/**
* 简单查询lock表的信息( 不常用数据 )
* @param id
* @return
*/
public Lock queryLockByLockId(Integer id);
}
LockMapper.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.atguigu.mapper.LockMapper">
<!-- /**
* 简单查询lock表的信息( 不常用数据 )
* @param id
* @return
*/
public Lock queryLockByLockId(Integer id);-->
<select id="queryLockByLockId" resultType="com.atguigu.pojo.Lock">
select `id`,`name` from t_lock where id = #{id}
</select>
</mapper>
KeyMapper接口:
/**
* 要两步查询key的信息,一次只查key表的数据<br/>
* @param keyId
* @return
*/
public Key queryKeyByKeyIdForTwoStep(Integer keyId);
KeyMapper.xml配置文件:
<resultMap id="queryKeyByKeyIdForTwoStepResultMap" type="com.atguigu.pojo.Key">
<id column="id" property="id" />
<result column="name" property="name"/>
<!--
需要调用association标签来调用一个查询从而得到子对象的数据
property是子对象的属性名
select 表示调用查询( 由名称空间+id组成 )
column 属性把指定列的值传递给调用的方法做参数值
-->
<association property="lock" column="lock_id"
select="com.atguigu.mapper.LockMapper.queryLockByLockId"
/>
</resultMap>
<!-- /**
* 要两步查询key的信息,一次只查key表的数据<br/>
* @param keyId
* @return
*/
public Key queryKeyByKeyIdForTwoStep(Integer keyId);-->
<select id="queryKeyByKeyIdForTwoStep"
resultMap="queryKeyByKeyIdForTwoStepResultMap">
select `id`,`name`,`lock_id` from t_key where id = #{id}
</select>
测试的代码:
@Test
public void queryKeyByKeyIdForTwoStep(){
SqlSession session = sqlSessionFactory.openSession();
try {
KeyMapper keyMapper = session.getMapper(KeyMapper.class);
Key key = keyMapper.queryKeyByKeyIdForTwoStep(1);
System.out.println(key);
} finally {
session.close();
}
}
延迟加载
延迟加载在一定程序上可以减少很多没有必要的查询。给数据库服务器提升性能上的优化。
要启用延迟加载,需要在mybatis-config.xml配置文件中,添加如下两个全局的settings配置。
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消极加载 按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
多对一、一对多的使用示例
- 创建一对多数据库
## 一对多数据表
## 创建班级表
create table t_clazz(
`id` int primary key auto_increment,
`name` varchar(50)
);
## 插入班级信息
insert into t_clazz(`name`) values('javaEE');
insert into t_clazz(`name`) values('C++');
insert into t_clazz(`name`) values('H5');
insert into t_clazz(`name`) values('LUA');
## 创建学生表
create table t_student(
`id` int primary key auto_increment,
`name` varchar(50),
`clazz_id` int,
foreign key(`clazz_id`) references t_clazz(`id`)
);
## 插入班级信息
insert into t_student(`name`,`clazz_id`) values('javaEE_1',1);
insert into t_student(`name`,`clazz_id`) values('javaEE_2',1);
insert into t_student(`name`,`clazz_id`) values('javaEE_3',1);
insert into t_student(`name`,`clazz_id`) values('C++_1',2);
insert into t_student(`name`,`clazz_id`) values('C++_2',2);
insert into t_student(`name`,`clazz_id`) values('H5_1',3);
collection标签 一对多,立即加载
public class Student {
private Integer id;
private String name;
public class Clazz {
private Integer id;
private String name;
private List<Student> stus;
Mapper接口:
public interface ClazzMapper {
/**
* 一次性查询出班级和此班级所有学生信息
* @param clazzId 班级编号
* @return
*/
public Clazz queryClazzByClazzIdForSimple(Integer clazzId);
}
mapper.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.atguigu.mapper.ClazzMapper">
<resultMap id="queryClazzByClazzIdForSimpleResultMap"
type="com.atguigu.pojo.Clazz">
<id column="id" property="id" />
<result column="name" property="name" />
<!--
collection标签表示配置集合属性
property 配置集合的名称
ofType 配置集合中每个元素的具体类型
-->
<collection property="stus" ofType="com.atguigu.pojo.Student">
<!--
对于集合,我们需要区分主键列和非主键列
-->
<id column="stu_id" property="id" />
<result column="stu_name" property="name" />
</collection>
</resultMap>
<!-- /**
* 一次性查询出班级和此班级所有学生信息
* @param clazzId 班级编号
* @return
*/
public Clazz queryClazzByClazzIdForSimple(Integer clazzId);-->
<select id="queryClazzByClazzIdForSimple"
resultMap="queryClazzByClazzIdForSimpleResultMap">
select
t_clazz.*,
t_student.id stu_id,
t_student.name stu_name
from
t_clazz left join t_student
on
t_clazz.id = t_student.clazz_id
where
t_clazz.id = #{clazzId}
</select>
</mapper>
测试的代码:
package com.atguigu.mapper.test;
import com.atguigu.mapper.ClazzMapper;
import com.atguigu.pojo.Clazz;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
public class ClazzMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() throws IOException {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void queryClazzByClazzIdForSimple() {
SqlSession session = sqlSessionFactory.openSession();
try {
// 获取班级Dao / mapper
ClazzMapper clazzMapper = session.getMapper(ClazzMapper.class);
Clazz clazz = clazzMapper.queryClazzByClazzIdForSimple(1);
System.out.println(clazz);
} finally {
session.close();
}
}
}
一对多,赖加载
分两次查,最后一次用的时候再查,不用不查.
StudentMapper接口:
public interface StudentMapper {
/**
* 根据班级编号查询学生信息
* @param clazzId
* @return
*/
public List<Student> queryStudentsByClazzId(Integer clazzId);
}
StudentMapper.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.atguigu.mapper.StudentMapper">
<!--
* 根据班级编号查询学生信息
* @param clazzId
* @return
*/
public List<Student> queryStudentsByClazzId(Integer clazzId);-->
<select id="queryStudentsByClazzId" resultType="com.atguigu.pojo.Student">
select `id`,`name` from t_student where clazz_id = #{clazzId}
</select>
</mapper>
ClazzMapper接口
/**
* 根据班级编号查询班级信息, 分两次查,一次只查班级,一个查班级学生
* @param clazzId
* @return
*/
public Clazz queryClazzByClazzIdForTwoStep(Integer clazzId);
ClazzMapper.xml配置文件
<!--将查询回来的结果封装成为Clazz班级对象-->
<resultMap id="queryClazzByClazzIdForTwoStepResultMap"
type="com.atguigu.pojo.Clazz">
<id column="id" property="id" />
<result column="name" property="name" />
<!--
collection标签可以把查询回来的数据封装成为集合,
也可以通过调用一个查询得到集合
property表示集合属性名称
select 属性表示要调用哪个查询( 名称空间+id值 )
column表示传递给调用方法的参数( 设置列名 )
-->
<collection property="stus" column="id"
select="com.atguigu.mapper.StudentMapper.queryStudentsByClazzId"
/>
</resultMap>
<!-- /**
* 根据班级编号查询班级信息, 分两次查,一次只查班级,一个查班级学生
* @param clazzId
* @return
*/
public Clazz queryClazzByClazzIdForTwoStep(Integer clazzId);-->
<select id="queryClazzByClazzIdForTwoStep"
resultMap="queryClazzByClazzIdForTwoStepResultMap">
select `id`,`name` from t_clazz where id = #{id}
</select>
多列传值(了解)
多列传值是指在 association标签中或collection标签中调用的查询有多个参数如何传值.
动态SQL语句
动态的sql语句 ,是指mybatis通过在运行时动态的判断参数的值.从而改变sql的内容.叫动态sql.
准备工作:
public class User {
private int id;
private String lastName;
private int sex;
if 语句
说明: if语句,可以动态的根据你的值来决定,是否需要动态的添加查询条件。
public interface UserMapper {
/**
* 根据User对象 name属性 和 sex属性进行查询用户 <br>
* @param user
* @return
*/
public List<User> queryUsersByUser(User user);
}
mapper.xml配置文件:
<select id="queryUsersByUser"
parameterType="com.atguigu.pojo.User"
resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
where
<!--
if 判断.如果成立,就执行
-->
<if test="lastName != null">
last_name like concat('%',#{lastName},'%')
</if>
<if test="sex == 1 or sex == 0">
and
sex = #{sex}
</if>
</select>
测试代码:
@Test
public void queryUsersByUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.queryUsersByUser(new User(null, "xxx",10)).forEach(System.out::println);
} finally {
session.close();
}
}
where 语句
说明: where语句,可以帮我们在多个动态语句中,有效的去掉前面的多余的and 或 or 之类的多余关键字
mapper.xml配置文件:
<select id="queryUsersByUser"
parameterType="com.atguigu.pojo.User"
resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
<!--
where标签可以去掉包含的内容前面的and 或 or关键字. 并且如果有内容还会添加where关键字
-->
<where>
<!--
if 判断.如果成立,就执行
-->
<if test="lastName != null">
last_name like concat('%',#{lastName},'%')
</if>
<if test="sex == 1 or sex == 0">
and
sex = #{sex}
</if>
</where>
</select>
测试的代码:
@Test
public void queryUsersByUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.queryUsersByUser(new User(null, null,10)).forEach(System.out::println);
} finally {
session.close();
}
}
trim语句
说明: trim 可以动态在包含的语句前面和后面添加内容。也可以去掉前面或者后面给定的内容
prefix 前面添加内容
suffix 后面添加内容
suffixOverrides 去掉的后面内容
prefixOverrides 去掉的前面内容
<select id="queryUsersByUser"
parameterType="com.atguigu.pojo.User"
resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
<!--
trim 标签可以去掉包含内容前面 ,或 后面指定的内容.
也可以在包含内容的前面 或 后面 添加指定的内容
prefix 前面添加内容
suffix 后面添加内容
suffixOverrides 去掉的后面内容
prefixOverrides 去掉的前面内容
-->
<trim suffixOverrides="and" prefixOverrides="and" prefix="where">
<!--
if 判断.如果成立,就执行
-->
<if test="lastName != null">
last_name like concat('%',#{lastName},'%') and
</if>
<if test="sex == 1 or sex == 0">
sex = #{sex}
</if>
</trim>
</select>
choose( when , otherwise )语句
说明:choose when otherwise 可以执行多路选择判断,但是只会有一个分支会被执行。
Mapper接口:
public List queryUsersByUserChooseWhenOtherWise(User user);
mapper.xml配置文件:
<select id="queryUsersByUserChooseWhenOtherWise"
parameterType="com.atguigu.pojo.User"
resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
<choose>
<when test="lastName != null">
where last_name like concat('%',#{lastName},'%')
</when>
<when test="sex == 0 or sex == 1">
where sex = #{sex}
</when>
<otherwise>
where 1 = 1
</otherwise>
</choose>
</select>
测试的代码:
@Test
public void queryUsersByUserChooseWhenOtherWise(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = userMapper.queryUsersByUserChooseWhenOtherWise(new User(null, null, 10));
users.forEach(System.out::println);
} finally {
session.close();
}
}
set语句
删除条件后的逗号 , 主要应用于 update 语句中
mapper.xml配置文件:
<!-- public int updateUser(User user);-->
<update id="updateUser" parameterType="com.atguigu.pojo.User">
update
t_user
<!-- 可以去掉,包含内容后面的逗号,如果有包含的内容.还会添加set关键字 -->
<set>
`last_name` = #{lastName} ,
<if test="sex == 0 or sex == 1">
`sex` = #{sex}
</if>
</set>
where
id = #{id}
</update>
测试的代码:
@Test
public void updateUser(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.updateUser(new User(16, "updated", 10));
session.commit();
} finally {
session.close();
}
}
foreach语句
foreach很直观就是遍历
一: 遍历查询条件
select * from 表名 where id in(xx,xx,xx)
Mapper接口:
// 一: 遍历查询条件
// select * from 表名 where id in(xx,xx,xx)
public List<User> queryUsersByIds(List<Integer> ids);
Mapper.xml配置文件:
<!-- // 一: 遍历查询条件
// select * from 表名 where id in(xx,xx,xx)
public List<User> queryUsersByIds(List<Integer> ids);-->
<select id="queryUsersByIds" resultType="com.atguigu.pojo.User">
select
`id`,`last_name` lastName,`sex`
from
t_user
<!--
collection 是遍历的数据源
item 是遍历到的数据
separator 遍历的每个元素中的间隔符
open遍历前输出的内容
close遍历后输出的内容
-->
<foreach collection="list" item="id" separator="," open="where id in (" close=")">
#{id}
</foreach>
</select>
测试的代码:
@Test
public void queryUsersByIds(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(11);
ids.add(12);
List<User> users = userMapper.queryUsersByIds(ids);
users.forEach(System.out::println);
} finally {
session.close();
}
}
二:遍历批量插入
insert into 表名(列,列) values(?,?,?),(?,?,?)
Mapper接口
// 二:遍历批量插入
// insert into 表名(列,列) values(?,?,?),(?,?,?)
public int saveUsers(List<User> users);
mapper.xml配置文件:
<!-- // 二:遍历批量插入
// insert into 表名(列,列) values(?,?,?),(?,?,?)
public int[] saveUsers(List<User> users);-->
<insert id="saveUsers">
insert into
t_user(`last_name`,`sex`)
values
<foreach collection="list" item="user" separator=",">
(#{user.lastName},#{user.sex})
</foreach>
</insert>
测试的代码:
@Test
public void saveUsers(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = new ArrayList<>();
users.add(new User(null,"aaa", 1));
users.add(new User(null,"bbb", 1));
users.add(new User(null,"ccc", 1));
userMapper.saveUsers(users);
session.commit();
} finally {
session.close();
}
}
sql片段
sql片段是我们抽取了多个sql语句中相同的公共部分的内容.用于统一维护的sql叫片段.
mybatis缓存
缓存: 缓存是指把经常访问的数据保存到高速缓冲区中.
缓存名词又可以理解为,保存中高速缓冲区中的数据.
常见的做法,就是把数据保存起来.方便快速访问.
一级缓存: 同一个sqlSession是一级缓存
二级缓存: 同一个SqlsessionFactory是二级缓存
mybatis的一级缓存的示例
测试的代码:
@Test
public void testFirstCache() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
User user1 = userMapper.queryUserById(1);
System.out.println(user1);
} finally {
session.close();
}
}
- 一级缓存的管理
缓存失效的四种情况:
1.不在同一个SqlSession对象中
@Test
public void queryOne() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
} finally {
session.close();
}
}
// 缓存失效的四种情况:
// 1.不在同一个SqlSession对象中
@Test
public void testFirstCacheFail1(){
queryOne();
queryOne();
}
2.执行语句的参数不同。缓存中也不存在数据。
@Test
public void testFirstCacheFail2(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
User user1 = userMapper.queryUserById(2);
System.out.println(user1);
} finally {
session.close();
}
}
3.执行增,删,改,语句,会清空掉缓存
// 3.执行增,删,改,语句,会清空掉缓存
@Test
public void testFirstCacheFail3(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
// 只要两次查询中,执行了写操作,,, insert,delete,update操作,缓存就会被清空
userMapper.insertUser(new User(null,"abc", 1));
User user1 = userMapper.queryUserById(1);
System.out.println(user1);
session.commit();
} finally {
session.close();
}
}
4.手动清空缓存数据
// 4.手动清空缓存数据
@Test
public void testFirstCacheFail4(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
// clear 清空
// cache缓存
session.clearCache();
User user1 = userMapper.queryUserById(1);
System.out.println(user1);
session.commit();
} finally {
session.close();
}
}
mybatis的二级缓存
二级缓存的图解示意
二级缓存的使用:
myBatis的二级缓存默认是不开启的。
1 我们需要在mybatis的核心配置文件中配置setting选项
2 在Mapper.xml的配置文件中加入cache标签。启动二级缓存
3 被二级缓存的对象必须要实现java的序列化接口。
二级缓存的演示
public void queryOne() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
} finally {
session.close();
}
}
@Test
public void testSecondCache(){
queryOne();
queryOne();
}
useCache="false"的演示和说明
useCache属性是在select标签中设置查询的结果是否缓存到二级缓存中.默认情况下它的值是true,表示保存到二级缓存中.
flushCache="false"的演示和说明
当执行insert / update / delete 这些语句的时候,都会清空缓存.
也可以通过在这些标签上使用属性 flushCache=“false” 设置不清空缓存.
如何自定义二级缓存 ( 知道 )
1 实现Mybatis中提供的Cache接口
public class MyCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap();
public MyCache(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return this.cache.size();
}
/**
* 保存数据到二级缓存中
* @param key
* @param value
*/
public void putObject(Object key, Object value) {
System.out.println("保存数据到二级缓存 key : " + key);
System.out.println("保存数据到二级缓存 value : " + value);
this.cache.put(key, value);
}
/**
* 从二级缓存中取数据
* @param key
* @return
*/
public Object getObject(Object key) {
System.out.println("从二级缓存中取数据 key : " + key);
return this.cache.get(key);
}
public Object removeObject(Object key) {
return this.cache.remove(key);
}
public void clear() {
this.cache.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public boolean equals(Object o) {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if (this == o) {
return true;
} else if (!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}
public int hashCode() {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}
2 到Mapper.xml中去配置自定义的二级缓存.
缓存的使用顺序说明:
1、当我们执行一个查询语句的时候。mybatis会先去二级缓存中查询数据。
2 如果二级缓存中没有。就到一级缓存中取数据
3、如果二级缓存和一级缓存都没有。就发sql语句到数据库中去查询。
4、查询出来之后马上把数据保存到一级缓存中。
5、当SqlSession关闭的时候,会把一级缓存中的数据保存到二级缓存中。
mybatis 逆向工程
MyBatis逆向工程,简称MBG。是一个专门为MyBatis框架使用者定制的代码生成器。可以快速的根据表生成对应的映射文件,接口,以及Bean类对象。
逆向工程只能对单表生成CRUD操作
叫 mybatis-generator-core-1.3.2。
它可以帮我们对比数据库表之后,生成大量的这个基础代码。
这些基础代码有:
1、数据库表对应的javaBean对象
2、这些javaBean对象对应的Mapper接口
3、这些Mapper接口对应的配置文件
<!-- 去掉全部的注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
准备数据库表
create database mbg;
use mbg;
create table t_user(
`id` int primary key auto_increment,
`username` varchar(30) not null unique,
`password` varchar(40) not null,
`email` varchar(50)
);
insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@atguigu.com');
insert into t_user(`username`,`password`,`email`) values('wzg168','123456','admin@atguigu.com');
insert into t_user(`username`,`password`,`email`) values('admin168','123456','admin@atguigu.com');
insert into t_user(`username`,`password`,`email`) values('lisi','123456','admin@atguigu.com');
insert into t_user(`username`,`password`,`email`) values('wangwu','123456','admin@atguigu.com');
create table t_book(
`id` int primary key auto_increment,
`name` varchar(50),
`author` varchar(50),
`price` decimal(11,2),
`sales` int,
`stock` int
);
## 插入初始化测试数据
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , 'java从入门到放弃' , '国哥' , 80 , 9999 , 9);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53);
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` )
values(null , '赌神' , '龙伍' , 66.5, 125 , 535);
select * from t_user;
select * from t_book;
逆向工程配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime 可以设置生成的版本
MyBatis3 豪华版 除了CRUD还有很多有用的查询方法
MyBatis3Simple 标配版 只有CRUD
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 去掉全部的注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--
修改 数据库的 连接属性
-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mbg"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--配置生成javaBean的
targetPackage javaBean的包名
targetProject 生成在哪个项目目录下
-->
<javaModelGenerator targetPackage="com.atguigu.pojo" targetProject=".\08-mybatis-mbg\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--
配置生成的Mapper.xml配置文件
targetPackage javaBean的包名
targetProject 生成在哪个项目目录下
-->
<sqlMapGenerator targetPackage="com.atguigu.mapper" targetProject=".\08-mybatis-mbg\src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--
配置生成Mapper接口
targetPackage Mapper接口的包名
targetProject 生成在哪个项目目录下
-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mapper"
targetProject=".\08-mybatis-mbg\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--
数据库一个表对应一个table标签
tableName是表名
domainObjectName 设置生成的类名
-->
<table tableName="t_user" domainObjectName="User" />
<table tableName="t_book" domainObjectName="Book" />
</context>
</generatorConfiguration>
执行逆向工程的代码:
package com.atguigu.mbg.runner;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MbgRunner {
public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("08-mybatis-mbg/mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
土豪金版本的测试代码:
package com.atguigu.test;
import com.atguigu.mapper.BookMapper;
import com.atguigu.pojo.Book;
import com.atguigu.pojo.BookExample;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
public class BookMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() throws IOException {
sqlSessionFactory= new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void countByExample() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
// 创建一个条件类
BookExample bookExample = new BookExample();
// 创建一个查询条件
BookExample.Criteria criteria = bookExample.createCriteria();
// where xxx = xxx and xxx = xxx;
// where xxx = xxx or xxx = xxx;
// 查询价格大于16的图书有几条记录
// criteria.andPriceGreaterThan(new BigDecimal(16));
// 查询销量大于10 , 且 库存 大于50的记录
// criteria.andSalesGreaterThan(10);
// criteria.andStockGreaterThan(50);
// 查询销量大于10 , 或 库存 大于50的记录
criteria.andSalesEqualTo(10);
bookExample.or().andStockGreaterThan(50);
// count() 用于查询数量的方法
// count 用于查询数量的
// ByExample按条件来查询
int i = mapper.countByExample(bookExample);
System.out.println(i);
} finally {
session.close();
}
}
@Test
public void deleteByExample() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
// mapper.deleteByExample(null);// 只要条件是空,就是全表操作
BookExample bookExample = new BookExample();
bookExample.createCriteria().andSalesGreaterThan(10000);
mapper.deleteByExample(bookExample);
session.commit();
} finally {
session.close();
}
}
@Test
public void deleteByPrimaryKey() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
mapper.deleteByPrimaryKey(8);
session.commit();
} finally {
session.close();
}
}
@Test
public void insert() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
mapper.insert(new Book(null,"拐跑别人媳妇", "陈凯", new BigDecimal(1234), 1234,1234));
mapper.insert(new Book(null,"伟德渣男记", "王灿侨", new BigDecimal(1234), 1234,1234));
session.commit();
} finally {
session.close();
}
}
@Test
public void insertSelective() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = new Book(null,"国哥是怎样炼成的", null,null,100,100);
// 方法末尾Selective就表示操作的时候,不带null值的列
// mapper.insertSelective(book);// null列不在操作范围内
mapper.insert(book);// null列一样在操作范围内
session.commit();
} finally {
session.close();
}
}
@Test
public void selectByExample() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
BookExample bookExample = new BookExample();
bookExample.createCriteria().andIdBetween(1,10);
// 设置排序字段
bookExample.setOrderByClause(" price desc ");
// List<Book> books = mapper.selectByExample(null);// 条件为null,就整表操作
List<Book> books = mapper.selectByExample(bookExample);// 有条件
books.forEach(System.out::println);
} finally {
session.close();
}
}
@Test
public void selectByPrimaryKey() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = mapper.selectByPrimaryKey(6);
System.out.println(book);
} finally {
session.close();
}
}
@Test
public void updateByExampleSelective() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = new Book(null,"忽略null的列", null, null,1234,124);
BookExample bookExample = new BookExample();
bookExample.createCriteria().andIdGreaterThan(8);
/**
* 根据条件来进行更新,而且不带null的列
*/
mapper.updateByExampleSelective(book,bookExample);
session.commit();
} finally {
session.close();
}
}
@Test
public void updateByExample() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = new Book(null,"忽略null的列", null, null,1234,124);
BookExample bookExample = new BookExample();
bookExample.createCriteria().andIdGreaterThan(8);
/**
* 根据条件来进行更新,而且不带null的列
*/
mapper.updateByExample(book,bookExample);
session.commit();
} finally {
session.close();
}
}
@Test
public void updateByPrimaryKeySelective() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = new Book(null,"忽略null的列", null, null,1234,124);
/**
* 根据条件来进行更新,而且不带null的列
*/
mapper.updateByPrimaryKeySelective(book);
session.commit();
} finally {
session.close();
}
}
@Test
public void updateByPrimaryKey() {
SqlSession session = sqlSessionFactory.openSession();
try {
BookMapper mapper = session.getMapper(BookMapper.class);
Book book = new Book(null,"忽略null的列", null, null,1234,124);
/**
* 根据条件来进行更新
*/
mapper.updateByPrimaryKey(book);
session.commit();
} finally {
session.close();
}
}
}