- 1. 入门案例
- 二、环境搭建的注意事项
- 三、入门案例
- 四、使用注解的方式
- 2. 增删改
- 一、增
- 1、在dao接口中创建一个抽象方法
- 2、在映射配置文件中
- 3、在执行的时候一定不要忘记sqlSession.commit()
- 二、保存操作的细节,获取保存数据的id
- 1、在dao接口中创建一个方法
- 2、在映射文件中这样写
- 3、然后再注册该mapper,
- 4、在测试类写的注意事项
- 三、改
- 1、在UserDao接口上新建一个方法
- 2、在映射配置文件中写sql
- 3、在主配置文件中注册上
- 4、在测试文件中写测试方法
- 四、删除
- 1、给UserDao接口上新建一个方法
- 2、在映射配置文件中写sql
- 3、在主配置文件中注册
- 4、在测试文件中写测试方法
- 3. 查询
- 一、根据Id查询用户
- 二、Like查询
- 4、Test类
- 三、详解Like 查询中 #{} 和 ‘${value}’的差距
- 四、查询总用户 (一行一列型)
- 4、测试方法
- 4. 输入类型/resultMap/Properties/typeAliases
- 一、parameterType (输入类型)
- 二、resultMap(结果类型)
- 三、Properties
- 四、typeAliases
- 五、MyBatis连接池
- 5. 事务/动态sql/多表查询
- 一、MyBatis事务(spring时再讲)
- 二、动态sql
- 三、多表查询
1. 入门案例
新建一个maven项目
选择:maven-archetype-quickstart
一、环境搭建
1、依赖
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.4</version></dependency>
2、创建实体类
实体类属性和表中的字段一致实现Serializable接口【序列化接口】该重写的方法都重写了
3、创建该类的接口,定义抽象方法
// 接口 操作User表public interface UserDao {// 返回List集合 集合中保存的是User对象List<User> getUser();}
4、创建主配置文件
在resources目录下创建 mybatis-config.xml文件
官网: https://mybatis.org/mybatis-3/zh/getting-started.html
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml"/></mappers></configuration>
5、创建映射配置文件UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="org.mybatis.example.BlogMapper"><select id="selectBlog" resultType="Blog">select * from Blog where id = #{id}</select></mapper>
6、测试

二、环境搭建的注意事项
在MyBatis中他把持久层的操作接口名称和映射文件也叫做:Mapper
所以UserDao.java 这个接口和UserMapper.xml是一致的
在IDEA创建目录的时候,他和包是不一样的
- 包:com.yixuexi.dao 三层结构
- 目录:com/yixuexi/dao 一层目录 【所以要分三次创建】
4. MyBatis的映射配置文件 必须和dao结构的包结构相同
也就是说在resources文件夹下建出和dao同样的结构目录然后mapper放进去
为什么在resources里面搞Mapper?
因为 main—>java 目录下的xml文件不会被编译
在一定要用/
在一定要用/
5. 映射文件的Mapper标签的namespace属性的取值必须是dao接口的全限定类名
6. 映射配置文件的操作配置,id属性的取值必须是dao接口的方法名
当遵从了第以上的要求之后,在开发中就不需要再去写dao的实现类
7. 映射配置文件的resultType=”com.yixuexi.entity.User“ 是说 查询出来的结果封装到这个实体类中
全类名
三、入门案例
1、使用xml的方式
当配置好了mybatis的主配置文件和 Mapper之后 就可以进行编写java代码了
1、读取配置文件
不建议使用绝对路径和相对路径
建议使用(有bug为解决)
- 类加载器读取
String path = Thread.currentThread().getContextClassLoader().getResource("mybatis-config.xml").getPath();
- ServletContext对象的getRealPath()方法
2、创建SqlSessionFactory工厂
mybatis使用了构建者模式[设计模式]
builer就是构建者,调用builer的builer()方法传进去一个流 即可
把对象的创建细节隐藏,使用者直接调用方法即可拿到对象
3、创建SqlSession
- 生产SqlSession使用了工厂模式[解耦合]
4、创建Dao接口的代理对象
创建DAO接口实现类 使用了代理模式[不修改源码的基础上对已有的方法增强]
5、执行dao中的方法
6、释放资源

2、注意事项
- 千万不要忘记在Mapper.xml 也就是映射文件中要告诉要封装到那个实体类中
<select id="getUser" resultType="com.yixuexi.entity.User">
四、使用注解的方式
使用注解的话 主配置文件还是要有的 mybatis-config.xml 不能动
但是Mapper.xml文件就可以不创建了
比xml文件更加的便捷
在接口中的方法上面使用@Select(“select * from user”)
public interface UserDao {@Select("select * from user")List<User> getUsers();}
然后在主配置文件中注册上
<mapper class="com.yixuexi.dao.UserDao"></mapper>
要求使用全限定类型(代包名)【把那个接口的全名写到 class属性中】

2. 增删改
一、增
在环境搭建好的基础上
1、在dao接口中创建一个抽象方法
public interface UserDao {int saveUser(User user);}
2、在映射配置文件中
<!--namespace 写dao接口的全名 代包名--><mapper namespace="com.yixuexi.dao.UserDao"><!--id 写dao接口中的方法名字 不带()--><!--parameterType 写对应那个实体类 从那个实体类中取数据 输入参数类型--><insert id="saveUser" parameterType="com.yixuexi.entity.User"><!--取数据用 #{属性名或者getId() 都可以}-->insert into user(id,name,password) values(#{id},#{name},#{password})</insert></mapper>
3、在执行的时候一定不要忘记sqlSession.commit()
因为自动提交事务是关闭的
二、保存操作的细节,获取保存数据的id
Select last_insert_id(); 会查询到最后一次保存的id
1、在dao接口中创建一个方法
//保存一个User 并且返回该User的idInteger saveUserReturnId(User user);
2、在映射文件中这样写
<!--id写到接口中方法名 parameterType输入参数类型--><!--useGeneratedKeys:设置是否使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中--><insert id="saveUserReturnId" keyProperty="id" useGeneratedKeys="true" parameterType="com.yixuexi.entity.User">insert into user(name,password) values(#{name},#{password})</insert>
3、然后再注册该mapper,
(已经注册过的不需要在注册)
4、在测试类写的注意事项
返回的值并不是id属性
要想得到id属性从user对象中getId即可
一定不要忘记 session.commit();
id必须为主键并且自增长 auto_increment

三、改
在环境搭建好的基础上
1、在UserDao接口上新建一个方法
public interface UserDao {//用户更改的方法void updateUser(User user);}
2、在映射配置文件中写sql
<update id="updateUser" parameterType="com.yixuexi.entity.User">update user set name = #{name},password = #{password} where id = #{id}</update>
3、在主配置文件中注册上
<mappers><mapper resource="com/yixuexi/dao/UserDaoMapper.xml"></mapper></mappers>
4、在测试文件中写测试方法
注意最后一定要SqlSession.commit(); // 提交
四、删除
在环境搭建好的基础上
1、给UserDao接口上新建一个方法
public interface UserDao {void deleteUser(Integer id);}
2、在映射配置文件中写sql
类型不是User而是Integer<update id="deleteUser" parameterType="java.lang.Integer">取值直接用#{}delete from user where id = #{id}</update>
3、在主配置文件中注册
<mappers><mapper resource="com/yixuexi/dao/UserDaoMapper.xml"></mapper></mappers>
4、在测试文件中写测试方法
注意最后一定要SqlSession.commit();
3. 查询
一、根据Id查询用户
1、在dao接口中
public interface UserDao {//查询一个的方法,根据id查询User findOne(Integer id);}
2、在映射配置文件中
<mapper namespace="com.yixuexi.dao.UserDao"><!--resultType写最后封装到哪里,parameterType 写值是什么类型--><select id="findOne" resultType="com.yixuexi.entity.User" parameterType="java.lang.Integer">select * from user where id = #{id}</select></mapper>
3、在主配置文件中注册
<mapper resource="com/yixuexi/dao/UserDaoMapper.xml"></mapper>
测试
二、Like查询
1、在接口中定义一个抽象方法
//模糊查询like [通过名字查询]List<User> findLike(String name);
2、在映射文件中写sql
<select id="findLike" resultType="com.yixuexi.entity.User" parameterType="String">select * from user where name like #{name}Select * from user where name like '%${value}%';也可以这样,不过这个value是固定的,% 是占位符【不常用】测试类中调用 方法的时候就不需要再提供 % 占位符了</select>
3、在主配置文件中配置
(如果配置了就不需要再配置了)
4、Test类
注意事项:再测试传值的时候 一定要带上占位符,因为在映射配置文件中不能写 % _List<User> like = mapper.findLike("%曹%");
三、详解Like 查询中 #{} 和 ‘${value}’的差距
#{}在传参的时候需要字符串添加 占位符 % _
使用的是预编译的数据库操作对象,更加安全 不会有
SQL注入PrepatedStatement
常用,但是不能用作排序查询
{%vlaue%}
使用的是 普通的数据库操作对象 可能会产生
SQL注入问题Statement
一般可以使用在 排序查询的时候,传进去 ${value} desc / asc
不管传进来的是什么 都是
${value}
select * from user order by id ${value}
四、查询总用户 (一行一列型)
1、在dao接口中添加方法
//测试查询总记录条数Integer countAll();
2、在映射配置文件中配置
只需要配置一个 结果界类型 resultType即可<select id="countAll" resultType="Integer">select count(*) from user</select>
3、在主配置文件中注册
如果该映射文件被注册了则不用进行注册
4、测试方法
4. 输入类型/resultMap/Properties/typeAliases
一、parameterType (输入类型)
1、传递简单的类型
int Integer String 都可以
2、pojo对象(实体类对象)
MyBatis使用ognl表达式解析对象字段的值,
Ognl:对象导航图语言
通过对象的取值方法来获取数据,在写法上把get省略了
例如:
- 类中的写法:user.getUsername();
- ognl表达式:user.username;
MyBatis为什么能直接写username,而不用user呢
因为在parameterType中已经提供了属性所属的类,此时不需要写对象名
传递pojo包装对象
- 有一个QueryVo类 里面有一个属性是 private User user 【提供了get set方法】
- 当查询时,在parameter=”com.yixuexi.entity.QueryVo” 那么下面的sql语句如何调用到 user对象的username
Select * ``from`` user where name = #{user.username}
直接点即可 因为ognl表达式,parameter已经提供了QueryVo
有多个对象组合在一起 完成查询
二、resultMap(结果类型)
(映射文件中的配置)
1、解决实体类属性和列名不一致的两种解决方案
1)第一种(执行效率快,开发效率低)
类中的属性为 userId,username,userPassword
为了能让数据库查询出来的数据 封装到User对象中去,可以采用起别名的方式
在映射xml文件中这样写sql
<select id="findAll" resultType="com.yixuexi.entity.User" parameterType="String">Select id as userId,name as username,password as userPassword from user;</select>
2)第二种 (执行效率低,开发效率变快)
- 采用MyBatis提供的一个配置
- 直接在dao映射文件中配置
<!--id 就是起个名,在使用的时候用这个名就行 type 是哪个实体类--><resultMap id="userMap" type="com.yixuexi.entity.User"><!--配置主键字段的对应 property 写类中的属性名 column写数据库中对应的字段名--><id property="userId" column="id"></id><!--非主键字段的对应--><result property="username" column="name"></result><result property="userPassword" column="password"></result></resultMap>
- 如果要使用resultMap对应的话,那么resultType则需要换成resultMap 值就是id
<select id="find" resultMap="userMap">select * from user;</select>
三、Properties
1、在主配置文件中 mybatis-config.xml中配置
<configuration><!-- 定义变量 --><properties><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="0000"/></properties><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!-- 使用变量 --><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="com/yixuexi/dao/UserDaoMapper.xml"></mapper></mappers></configuration>
2、引用外部文件配置
(可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件配置信息)
resources属性: 常用于指定配置文件的位置,是按照类路径的写法,并且必须存储在类路径下
要用到
properties标签中的**resources**属性 [在类的根路径下 src下]
<properties resource="mybatis.properties"></properties>
- Mybatis.properties中这样写
driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatisusername=rootpassword=0000
- 注意事项:
properties`中的信息`key`必须和 `dataSource`里面的的`value`中的`${一致}
四、typeAliases
- (主配置文件中的标签)
- 使用typeAliases配置别名,他只能配置 实体类 中类得别名,
1、typeAlias
<typeAliases><!--type:实体类的具体全限定类名 alias:别名--><typeAlias type="com.yixuexi.domain.User" alias="user"></typeAlias></typeAliases>
用了此标签后,resultType和parameterType不能在用全限定名字了,要使用alias别名
2、Package常用
<typeAliases><!--直接配置一个包,包下的所有类都会配置--><!--用于指定要配置的别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名--><package name="com.yixuexi.domain"/></typeAliases>
mapers标签下的package子标签(映射配置文件中的)
<mappers><!--1. 使用package的要求- mapper文件必须和dao接口名必须完全相同,包括大小写- mapper文件和dao文件必须在同一目录【mapper文件在resources目录下也行 前提是也有 com.xxx.xxx】2. package标签用于指定dao接口所在的包,当指定了之后就不需要在写mapper了 resouces和class也不用了--><package name="com.yixuexi.dao"/></mappers><!--不用插件也行,要符合上面的要求--><-- pom文件插件 --><!--配置资源插件,在java目录下的xml文件也会被编译到,放到最后那里就行--><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources>
五、MyBatis连接池
我们在实际开发中都会使用连接池
减少获取连接时所消耗的时间
mybatis提供了3中方式的配置
配置的位置
主配置文件 mybatis-config.xml中的dataSource标签的type属性
Type属性的取值:
POOLED(默认) :采用传统的javax.sql.DataSource规范中的连接池 mybatis中有针对它的实现、
UNPOOLED:传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是名没有使用池的思想
JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器之间不同
注意:如果是不web获取maven的war功能,是不能使用的
1、UNPOOLED和POOLED差别
1)UNPOOLED
通过源码可以看到 UNPOOLED是每次使用的执行sql的时候都会在底层进行一套JDBC操作
注册驱动,获取连接……
2)POOLED
使用的是池的思想
5. 事务/动态sql/多表查询
一、MyBatis事务(spring时再讲)
在创建sqlsession对象时传一个参数进去 true时 得到的SqlSession的事务自动提交时开启的
SqlSession sqlSession = ``**build.openSession(**``**true**``**);** ``(开发时用的少)
二、动态sql
1、 标签
在映射配置文件中这样写
<select id="findUserByCondition" resultType="com.yixuexi.entity.User" parameterType="com.yixuexi.entity.User">select * from user where 1 = 1 // 使用if标签 where 1=1不能省略<if test="name != null">and name = #{name}</if></select>
这里面的if 并且的关系是and 或者的关系是or
其实就是sql语句的拼接 在select语句后面 加个if就是拼接上if里面的内容
如果有多个条件 可以添加多个if
select * from user where 1 = 1<if test="name != null">and name = #{name}</if><if test="password != null">and password = #{password}</if>
多个if都会执行
2、where标签
- 使用where标签可以省略 where 1 = 1
- 就是在 if标签外面套一层 where包裹起来
<select id="findUserByCondition" resultType="com.yixuexi.entity.User" parameterType="com.yixuexi.entity.User">select * from user<where><if test="name != null">and name = #{name}</if><if test="password != null">and password = #{password}</if></where></select>
3、foreach标签
<select id="findUserByIds" parameterType="com.yixuexi.entity.QueryVoIds" resultType="com.yixuexi.entity.User">select * from user<where> <!--添加了where之后 就不用再手写 where 1=1 --><!--如果这个对象里面的list集合不是null,并且 list集合的个数大于0个 则遍历 --><if test="list != null and list.size > 0"><!--- collection:遍历哪个集合,集合名称- open:开头添加什么 添加 and id in(} open哪里加and是因为 where标签会帮你加 where 1=1 不加and不行- close:在结尾添加什么 添加 )- item:(起个名)把遍历出来的每一项添加到 open和close之间,用,进行分割- separator:用什么进行分割- #{id}:item起的什么名字 这里就用什么名字--><foreach collection="list" open="and id in(" close=")" item="id" separator=",">#{id}</foreach></if></where></select>
三、多表查询
1、一对一的数据库查询
1、创建Account子类的方式 (不常用)
SQL语句是这样的 (一个用户对一个账户)
SELECTa.* , u.name AS NAME ,u.password AS PASSWORDFROMaccount aJOINUSER uONa.uid = u.id
此时mybatis需要准备好两张表 一张Account表 和 一张继承Account的表 继承的表里面有需要查出来的别的字段
AccountUser子表 需要重写toString() 并且重写时,调用父类的toString() super.toString()
然后在AccountDao接口中创建一个方法
List``**<AccountUser**``> findAllAccountUser();返回值需是Account的子类,因为子类中有父类没有的属性 name 和 password
然后在映射文件中写sql语句
<select id="findAllAccountUser" resultType="AccountUser"><!--因为这里再主配置文件中配置了typeAliases所以直接写类名即可-->selecta.* , u.name ,u.passwordfromaccount ajoinuser uona.uid = u.id</select>
- 这里的数据库列名需要和实体类中的属性名一致,所以再创建子类的时候 属性名要写好
2、从表中有实体引用(常用)
1) 从表实体类中应该含一个主表实体的包对象引用
public class Account {private Integer id;private Integer uid;private Double money;//从表实体类中应该有一个主表的对象private User user;}
student学生表 引用了外键 class的id 班级表 那么说 student就是从表 class就是主表
2) SQL语句
selecta.*,u.id,u.name,u.passwordfromaccount ajoinuser uona.uid = u.id
3) 因为Account(子表)表中有一个主表的对象,那么再查询结果封装的时候是 没办法把User封装进去的,需要这样设置
<!--定义一个能封装account和user的resultMap--><resultMap id="accountUserMap" type="Account"><!--定义主键字段--><id property="id" column="id"></id><!--定义非主键字段--><result property="uid" column="uid"></result><result property="money" column="money"></result><!--一对一的关系映射:配置user的内容--><!--property:该实体类中的哪个对应关系的主表属性--><!--column:通过哪一个 字段进行获取的--><!--javaType:最后的结果封装到哪个类 正常写法是com.xxx.xx 用了TypeAlia..所以直接类名--><association property="user" column="uid" javaType="User"><!--在里面写User的属性和数据库对应起来--><id property="id" column="id"></id><result property="name" column="name"></result><result property="password" column="password"></result></association></resultMap>
4) sql语句原封不动的写就行
<select id="findAll" resultMap="**accountUserMap**">这里写上resultMap起的id名
2、一对多(一个用户有多个账户)
- 账户表的外键引用了用户表的主键,那么说账户表是从表,用户表示主表
- 一对多关系映射:主表实体中应该包含从表实体的集合引用
1、UserDao接口中的方法
List<User> findAll();
2、User实体类中的属性
public class User {private Integer id;private String name;private String password;//一对多关系映射:主表实体中应该包含从表实体的集合引用private List<Account> accounts;}
3、SQL语句
selectu.*,a.id as aid ,a.uid,a.moneyfromuser uleft joinaccount aonu.id = a.uid因为有两个id所以给account表中的id起一个别名
4、在映射配置文件中写对应关系
<!--定义User的resultMap 一对多查询--><!--id 随便起一个名字 type:类型仍然是一个User 配置了别名所以直接写类名--><resultMap id="UserAccountMap" type="User"><id property="id" column="id"></id><result property="name" column="name"></result><result property="password" column="password"></result><!--配置user对象中accounts集合的映射--><!--property:User对象的Account属性名--><!--ofType:集合中元素的类型(用了别名 不然要写权限定类名)--><!--一对多需要用 collection标签 --><collection property="accounts" ofType="Account"><id property="id" column="aid"></id><result property="uid" column="uid"></result><result property="money" column="money"></result></collection></resultMap>

- 为什么是column是aid 因为查询的有两个id 一个用户id 一个 账户id 为了区分在起了个别名
- 同时 sql语句也要写上 a.id as aid
5、编写测试类
3、多对多
多对多,三张表,关系表两个外键
示例:用户和角色
- 一个用户有多个角色
- 一个角色可以赋予多个用户
各自包含对方一个集合引用
- 当我们查询用户是,可以同时得到用户所包含的角色信息
- 当我们查询角色是,可以同时得到角色的所赋予的用户信息
1、SQL语句
查询身份并且显示对应的用户信息SELECTu.*,r.id AS role_id,r.role_name,r.role_descFROMUSER uJOINuser_role ur #连接关系表 条件是用户表的id等于关系表的uidONu.id = ur.uidJOINrole r ##连接身份表 条件是身份表的id等于关系表的ridONr.id = ur.rid数据库里面的id会有重复 所以要起一个别名,不过不要忘了配置文件的cloumn属性也改成别名
2、在RoleDao中创建一个方法
public interface RoleDao {/*** 查询所有角色* @return*/List<Role> findAll();}
3、Role实体类中的属性是这样的
public class Role implements Serializable {private Integer roleId;private String roleName;private String roleDesc;//多对多的关系映射,一个角色可以赋予多个用户private List<User> users;}
4、在RoleDaoMapper.xml文件中配置一下对应关系
<!--id就是起个名字 type:是什么类型,(全限定类名,但是又typeAlia)--><resultMap id="roleUserMap" type="Role"><!--配置主键 properties:类中的属性 column:数据库中的字段--><id property="roleId" column="role_id"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result><!--配置role对象中的users集合映射关系--><!--properties:写要配置哪个属性名--><!--ofType:集合里面是什么数据类型(全限定类名 …)--><collection property="users" ofType="User" ><id property="id" column="id"></id><result property="name" column="name"></result><result property="password" column="password"></result></collection></resultMap>
5、编写测试类
RoleDao mapper = session.getMapper(RoleDao.class);List<Role> all = mapper.findAll();for (Role role : all) {System.out.print(role);System.out.println(role.getUsers());}
- 在循环的时候调用一下
role.getUsers(); - 或者直接把users重写进
toString()// 这样会直接输出 list集合的toString()而哪个List集合的toString()已经重写

