mybatis知识
导入maven jar包
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
建立实体对象
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
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 + '\'' +
'}';
}
}
//创建持久层接口
package com.itheima.mapper;
import com.itheima.domain.User;
import java.util.List;
/**
* 持久层接口
*/
public interface IUserMapper {
/**
* 查询所有的用户信息
* @return
*/
public List<User> findAll();
/**
* 通过id查询用户
* @param id
* @return
*/
public User findById(Integer id);
/**
* 模糊查询
* @param userName
* @return
*/
public List<User> findByName(String userName);
/**
* 保持用户
* @param user
*/
public void saveUser(User user);
/**
* 修改账户
* @param user
*/
public void updateUser(User user);
/**
* 删除账户
* @param id
*/
public void deleteUser(Integer id);
}
//持久层配置文件 ---路径和持久层接口一样 com.itheima.mapper.IUserMapper.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.mapper.IUserMapper">
<!--查询所有用户 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from USER
</select>
<!--通过id查询用户 -->
<select id="findById" resultType="com.itheima.domain.User">
select * from user where id = #{id}
</select>
<!--保存账户信息 -->
<insert id="saveUser" parameterType="com.itheima.domain.User">
insert into user(username,birthday,sex,address)values(#{username},#{birthday},#{sex},#{address})
</insert>
<!--修改账户信息 -->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set
username = #{username},
birthday = #{birthday},
sex = #{sex},
address = #{address}
where id = #{id}
</update>
<!--通过id删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
<!--模糊查询 -->
<select id="findByName" resultType="com.itheima.domain.User" parameterType="java.lang.String">
select * from user where username like #{username}
</select>
</mapper>
//sqlMapConfig.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>
<!--配置mybatis环境 -->
<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/db3"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--数据库厂商标示 -->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
<!--databaseId="mysql" -->
<!--告知mybatis映射配置的位置 -->
<mappers>
<mapper class="com.itheima.mapper.IUserMapper"></mapper>
</mappers>
</configuration>
//数据的测试
com.itheima.text.UserMapperTest
package com.itheima.text;
import com.itheima.domain.User;
import com.itheima.mapper.IUserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class UserMapperTest {
InputStream in = null;
SqlSessionFactoryBuilder builder = null;
SqlSessionFactory factory = null;
SqlSession sqlSession = null;
IUserMapper userMapper = null;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
sqlSession = factory.openSession();
userMapper = sqlSession.getMapper(IUserMapper.class);
}
/**
* 查询所有用户信息
*/
@Test
public void testFindAll() {
List<User> users = userMapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
/**
* 通过查询用户信息
*/
@Test
public void testFindById(){
User user = userMapper.findById(41);
System.out.println(user);
}
/**
* 保存用户
*/
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("Anne");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("浙江");
user.setId(49);
userMapper.saveUser(user);
System.out.println(user);
}
/**
* 修改用户
*/
@Test
public void updateUser(){
User user = new User();
user.setId(53);
user.setUsername("Setiven");
user.setAddress("济南");
user.setSex("男");
user.setBirthday(new Date());
userMapper.updateUser(user);
}
/**
* 通过id删除用户
*/
@Test
public void testDeleteUser(){
userMapper.deleteUser(53);
}
/**
* 模糊查询
*/
@Test
public void testFindByName(){
List<User> users = userMapper.findByName("%王%");
for (User user : users) {
System.out.println(user);
}
}
@After
public void destroy() throws IOException {
sqlSession.commit(); //提交事务
sqlSession.close();
in.close();
}
}
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
<properties>
一般将数据源的信息单独放在一个properties文件中,然后用这个标签引入,在下面environment标签中,就可以用${}占位符快速获取数据源的信息
<settings>
用来开启或关闭mybatis的一些特性,比如可以用<setting name="lazyLoadingEnabled" value="true"/>来开启延迟加载,可以用<settings name="cacheEnabled" value="true"/>来开启二级缓存
<typeAliases>
在mapper.xml中需要使用parameterType和resultType属性来配置SQL语句的输入参数类型和输出参数类型,类必须要写上全限定名,比如一个SQL的返回值映射为Student类,则resultType属性要写com.yogurt.po.Student,这太长了,所以可以用别名来简化书写
select id="fuzzyCount" parameterType="string" resultType="int">
SELECT count(1) FROM `user` WHERE name like '%${value}%'
</select
修改数据配置文件
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
在SqlMapConfig.xml 环境配置中添加标签
<properties resource="jdbc.properties"></properties>
数据源修改为
<!--配置数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
修改别名
在SqlMapConfig.xml 环境配置中添加标签
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias type="com.itheima.domain.User" alias="user">
<!-- 批量别名定义,扫描整个包下的类,别名为类名,自动映射(大小写不敏感) -->
<package name="com.itheima" />
</typeAlias>
<!--package 为某个包下的所有类批量起别名 -->
name: 指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写))
<package name = "com.itheima.pojo">
<!--在批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
</typeAliases>
<!--
databaseIdProvider:支持多数据库厂商的;
type = "DB_VENDOR": 作用是得到数据库厂商的标识
-->
<databaseIdProvider type = "DB_VENDOR">
<!--为不同的数据库厂商起别名 -->
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="d2"/>
</databaseIdProvider>
<!--
-- databaseId= "mysql"定义数据库为MySQL
<select id="getAllProduct" resultType="product" databaseId="mysql">
SELECT * FROM product
</select>
-->
<!--传递两个参数 -->
public User findUserByIdAndUsername(@Param("id") Integer id, @Param("username") String username);
<select id="findUserByIdAndUsername" resultType="user">
select * from user where
id = #{id}
and
username = #{username}
</select>
@Test
public void testFindUserByIdAndUsername(){
User user = userMapper.findUserByIdAndUsername(50, "Jack");
System.out.println(user);
}
<!--封装为map -->
public User findUserByMap(Map<String,Object> map);
<select id="findUserByMap" resultType="user">
select * from user where id = #{id} and username = #{username}
</select>
@Test
public void testFindUserByMap(){
Map<String,Object> map = new HashMap<>();
map.put("id",50);
map.put("username","Jack");
User user = userMapper.findUserByMap(map);
System.out.println(user);
}
<!--返回一条记录为map key 为列名,值就是对应的值 -->
/**
* 返回map
* @param id
* @return
*/
public Map<String,Object> getUserByIdReturnMap(Integer id);
<!--返回Map -->
<select id="getUserByIdReturnMap" parameterType="java.lang.Integer" resultType="map">
select * from user where id = #{id}
</select>
@Test
public void testGetUserByIdReturnMap(){
Map<String, Object> map = userMapper.getUserByIdReturnMap(50);
for (String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
}
<!--key为id主键 value 为对象
@MapKey注解:告诉mybatis封装这个map的时候使用哪个属性作为主键
-->
@MapKey("id")
public Map<Integer,User> getUserByUserNameLikeReturnName(String username);
<select id="getUserByUserNameLikeReturnName" parameterType="java.lang.String" resultType="user">
select * from user where username like #{username}
</select>
@Test
public void testGetUserByUserNameLikeReturnName(){
Map<Integer, User> map = userMapper.getUserByUserNameLikeReturnName("%王%");
System.out.println(map);
}
动态SQL
if---
public List<User> findByCondition(User user);
一、
select * from user where 1= 1
<if test = "username != null">
username = #{username}
</if>
<if test = "birthday = != null">
birthday = #{birthday}
</if>
二、
public List<User> findByCondition(User user);
<select id="findByCondition" parameterType="user" resultType="user">
select * from user
<where>
<if test="username != null">
username = #{username}
</if>
<if test = "address = != null">
and address = #{address}
</if>
</where>
</select>
@Test
public void testFindByCondition(){
User user = new User();
user.setUsername("Jack");
user.setAddress("济南");
user.setBirthday(new Date());
user.setSex("男");
List<User> users = userMapper.findByCondition(user);
for (User user1 : users) {
System.out.println(user1);
}
}
<!--choose -->
public List<User> findByConditionChoose(User user);
//条件中任意一个只要满足
<select id="findByConditionChoose" resultType="user" parameterType="user">
select * from user
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="username != null">
username = #{username}
</when>
<when test="address != null">
address = #{address}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
@Test
public void testFindByConditionChoose(){
User user = new User();
user.setUsername("Jack");
user.setAddress("济南");
user.setBirthday(new Date());
List<User> users = userMapper.findByConditionChoose(user);
for (User user1 : users) {
System.out.println(user1);
}
}
动态foreach ---select * from user where id in(1,2,3)
public List<User> findByIds(List<Integer> ids);
<select id="findByIds" resultType="user" parameterType="list">
select * from user
<where>
<foreach collection="ids" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
SQL语句的抽取
<sql id ="selectUser">select * from user</sql>
代码中引入
<include refid = "selectUser"></include>
resultMap
resultMap标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装.
<!--
建立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>
pagehelper
导入
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
在 sqlMapConfig.xml中的
<configuration>
<!--配置分页助手插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
测试:
@Test
public void testFindAll() {
PageHelper.startPage(1,3); //1当前显示的页数 3 一页显示几条
List<User> users = userMapper.findAll();
for (User user : users) {
System.out.println(user);
}
//获取与分页有关的数据
PageInfo<User> pageInfo = new PageInfo<User>(users);
System.out.println("当前页" + pageInfo.getPageNum());
System.out.println("第一页:" + pageInfo.getFirstPage());
}
XML配置
一对一配置
在Account中添加User的实体
private User user;
<!-- 建立对应关系 -->
<resultMap type="account" id="accountMap">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定从表方的引用实体属性的 -->
<association property="user" javaType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
</select>
<!--
comlumn 数据库中表中字段
property 实体中的属性名
<association property = "" javaType = "">
property 当前实体中(Account)的属性名称(private User user)
javaType 当前实体中(Account)的属性的类型(User)
</association>
-->
一对多
在User实体中添加
private List<Account> accounts
<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection是用于建立一对多中集合属性的对应关系 ofType用于指定集合元素的数据类型 --> <collection property="accounts" ofType="account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid
</select>
注解开发
首选在SqlMapConfig.xml
<mappers>
<mapper>
<package name = "com.itheima.mapper">
</mapper>
</mappers>
public interface IUserMapper{
@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")
})
public List<User> findAll();
}
一对一
再Account中添加user的实体
private User user;
public interface IAccountMapper {
/** * 查询所有账户,采用延迟加载的方式查询账户的所属用户 * @return */
@Select("select * from account")
@Results(id="accountMap", value= {
@Result(id=true,column="id",property="id"),
@Result(column="uid",property="uid"),
@Result(column="money",property="money"),
@Result(column="uid", property="user", one=@One(select="com.itheima.dao.IUserDao.findById", fetchType=FetchType.LAZY) ) })
List<Account> findAll();
}
public interface IUserMapper{
@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") })
public List<User> findAll();
@Select("select * from user where id = #{id}")
@ResultMap("userMap")
public User findById(Integer id);
}
一对多
在user实体中添加account集合实体
private List<Account> accounts;
private interface IUserMapper{
@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"),
@Result(column="id",property="accounts",
many=@Many( select="com.itheima.dao.IAccountDao.findByUid", fetchType=FetchType.LAZY ) )
}
public List<User> findAll();
}
public interface IAccountMapper{
@Select("select * from account where uid = #{uid} ")
List<Account> findByUid(Integer userId);
}
SqlSession 的实例不是线程安全的,因此是不能被共享的。
SqlSession每次使用完成后需要正确关闭,这个关闭操作是必须的
SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以共安全的进行类型检查操作。
Mybatis-缓存机制
Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
Mybatis系统中默认定义了两级缓存。
一级缓存和二级缓存
默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
二级缓存需要手动开启和配置,他是基于namespace级别的缓存
为了提高扩展性,Mybatis定义了缓存借口的Cache 我们可以通过实现Cache来自定义二级缓存
一级缓存:
一级缓存即 本地缓存 作用域默认为: sqlSession 当Sesssion flush 或close后,该Session中的所有Cache将被清空。
本地缓存不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。
在mybatis3.1之后,可以配置本地缓存的作用域。在mybatis.xml中配置
mybatis利用本地缓存机制(local Cache) 防止循环引用和加速重复嵌套查询。默认值为SESSION 这种情况下回缓存一想回话中执行的所有查询。若设置值为STATEMENT 本地会话仅用在语句执行上,对相同sqlSession的不同调用将不会共享数据。
一级缓存演示& 失效情况
同一会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
-- key: hashCode+查询的SqlId+编写的Sql查询语句+参数
一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空的缓存
二级缓存,全局作用域缓存二级缓存默认不开启,需要手动配置
二级缓存默认不开启,需要手动配置
Mybatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
二级缓存在SqlSession关闭或提交之后才会生效
使用步骤:
全局配置文件中开启二级缓存
<settin name = "cacheEnabled" value = "true"/>
需要使用二级缓存的映射文件处使用cache配置缓存<cache/>
注意: POJO需要实现Serializable接口
主键返回
通常我们会将数据库表的主键id设为自增。在插入一条记录时,我们不设置其主键id,而让数据库自动生成该条记录的主键id,那么在插入一条记录后,如何得到数据库自动生成的这条记录的主键id呢?有两种方式
第一种: 使用useGeneratedKeys和keyProperty属性
<insert id="insert" parameterType="com.yogurt.po.Student" useGeneratedKeys="true" keyProperty="id">
INSERT INTO student (name,score,age,gender) VALUES (#{name},#{score},#{age},#{gender});
</insert>
第二种:
使用<selectKey>子标签
<insert id="insert" parameterType="com.yogurt.po.Student">
INSERT INTO student (name,score,age,gender) VALUES (#{name},#{score},#{age},#{gender});
<selectKey keyProperty="id" order="AFTER" resultType="int" >
SELECT LAST_INSERT_ID();
</selectKey>
</insert>
如果使用的是mysql这样的支持自增主键的数据库,可以简单的使用第一种方式;对于不支持自增主键的数据库,如oracle,则没有主键返回这一概念,而需要在插入之前先生成一个主键。此时可以用<selectKey>标签,设置其order属性为BEFORE,并在标签体内写上生成主键的SQL语句,这样在插入之前,会先处理<selectKey>,生成主键,再执行真正的插入操作。
<selectKey>标签其实就是一条SQL,这条SQL的执行,可以放在主SQL执行之前或之后,并且会将其执行得到的结果封装到入参的Java对象的指定属性上。注意<selectKey>子标签只能用在<insert>和<update>标签中。上面的LAST_INSERT_ID()实际上是MySQL提供的一个函数,可以用来获取最近插入或更新的记录的主键id。