主要内容
1.回顾mybatis的自定义在分析和环境搭建
2.完善基于注解的mybatis
3.mybatis基于dao代理实现crud
4.mybatis中参数深入及结果集深入
5.mybaits中基于传统dao方式实现CRUD
6.mybatis中的配置文件的一些标签
P1.回顾自定义及Mybatis环境搭建
第一步:
第二步:
第三步:
P1.Mybatis 基于Dao代理实现CRUD
add
1.Dao层添加保存操作
public interface UserDao {
List<User> findAll();
//保存用户
void saveUser(User user);
}
2.Dao.xml中配置sql
<mapper namespace="com.itheima.dao.UserDao">
<!-- 配置查询所有的接口对应的方法 和 SQL语句-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
<!-- 往数据库插入一条数据
id对应dao接口方法名
parameterType对应类名
values中 通过 #{类属性} 来获取对象的值
这里注意属性是get/set方法后缀
-->
<insert id="saveUser" parameterType="com.itheima.domain.user">
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{brithday});
</insert>
</mapper>
3.主程序
@Test
public void testSave() throws IOException {
User user = new User();
user.setUsername("jay");
user.setAddress("北京");
user.setSex("男");
user.setBirthday(new Date());
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法
userDao.saveUser(user);
//6.提交事务
session.commit();
session.close();
in.close();
}
注意:
1.插入语句没有写id,因为不知道id
2.这里注意手动提交事务;
如果要获取插入后的数据的id
插入后获取id
select last_insert_id();此SQL可以获取最新插入的数据的id
<insert id="saveUser" parameterType="com.itheima.domain.user">
<!-- keyProperty 指的是实体类的属性
keyColumn 对应的是数据库表的
order 在插入后还是擦汗如前获取值
-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{brithday});
</insert>
会自动封装到插入的对象中:
update/delete
(1)
public interface UserDao {
List<User> findAll();
//保存用户
void saveUser(User user);
//更新
void updateUser(User user);
//根据id删除用户
void deleteUser(Integer id);
}
(2)
<update id="updateUser" parameterType="com.itheima.domain.user">
update user set username = #{username},address=#{address},sex=#{sex},birthday= #{brithday} where id = #{id};
</update>
<!-- dao方法中只有一个integer类型的参数,#{xxx}里面只需要写一个占位符即可,写什么都可以 -->
<delete id="updateUser" parameterType="INTEGER">
delete from user where id = #{id};
</delete>
(3)主程序
*/
@Test
public void testUpdate() throws IOException {
User user = new User();
user.setId(46);
user.setUsername("jay");
user.setAddress("北京");
user.setSex("男");
user.setBirthday(new Date());
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法
userDao.updateUser(user);
//6.提交事务
session.commit();
session.close();
in.close();
}
@Test
public void testDelete() throws IOException {
User user = new User();
user.setId(46);
user.setUsername("jay");
user.setAddress("北京");
user.setSex("男");
user.setBirthday(new Date());
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法
userDao.deleteUser(49);
//6.提交事务
session.commit();
session.close();
in.close();
}
查询一个
//根据id查询
User findById(Integer id);
<!-- 根据id查询一个用户-->
<select id="findById" parameterType="INTEGER" resultType="com.itheima.domain.User">
select * from user where id = #{id};
</select>
@Test
public void testFindById() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法
User user1 = userDao.findById(45);
System.out.println(user1);
session.close();
in.close();
}
}
模糊查询 ‘$%{}%’ 和 #{}
//根据名字 模糊拆线呢
List<User> findByName(String username);
模糊查询方式1 #{}
<!-- 根据id查询一个用户-->
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username like #{name};
</select>
@Test
public void testFindByName() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法 注意提供百分号 这里
List<User> users = userDao.findByName("%王%");
for (User s : users) {
System.out.println(s);
}
session.close();
in.close();
}
注意:
模糊查询,传参需要提供百分号
List<User> users = userDao.findByName("%王%");
模糊查询方式2 ‘%${value}%’
<!-- 根据id查询一个用户-->
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username like '%${value}%';
</select>
这里 '%${value}%'是固定写法,必须为value
对应的测试类中不用再写百分号
@Test
public void testFindByName() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法 注意提供百分号 这里
List<User> users = userDao.findByName("王");
for (User s : users) {
System.out.println(s);
}
session.close();
in.close();
}
List users = userDao.findByName(“王”);
细节对比
#{}用的是PreparedStatement,所以更好!开发中更多用这种
‘$%{}%’ 用的是字符串拼接Sql
查询返回一行一列和占位符分析
//查询总用户数
int findTotal();
<!-- 查询总记录数 -->
<select id="findTotal" resultType="int">
select count(id) from user;
</select>
@Test
public void testCount() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法 注意提供百分号 这里
int total = userDao.findTotal();
System.out.println(total);
session.close();
in.close();
}
}
P2.Mybatis中参数深入
1.OGNL表达式
Object Graphic Navigation Language
对象 图 导航 语言
通过对象的取值方法(getter()
)来获取数据,在写法上把get给省略了
-
比如: 获取用户名称
类中写法:user.getUsername()
OGNL表达式写法:user.username -
Mybatis中为什么可以直接写username,而不用user.?
因为parameterType中已经提供了属性所属的类,此时不需要写对象,直接写属性名即可。如下图所示
2.parameterType(输入参数类型)
1.传递简单类型
2.传递pojo对象
3.将pojo对象包装起来查询
查询条件不是单独的一个pojo对象,而是封装了pojo对象;
也就是说涉及多表查询的情况下,需要对多个对象进行包装;
//根据queryVo中的条件查询用户数
int findUserByVo(QueryVo vo);
package com.itheima.domain;
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
<!-- 根据QueryVo的条件查询用户-->
<select id="findUserByVo" resultType="com.itheima.domain.User" parameterType="com.itheima.domain.QueryVo">
select * from user where username like #{user.username};
</select>
注意配置:
1. 传入的参数: parameterType="com.itheima.domain.QueryVo"
2. 模糊查询里的条件要加上封装类的属性(user属性) ,不能直接使用属性了!
@Test
public void testQueryVo() throws IOException {
QueryVo queryVo = new QueryVo();
User user = new User();
user.setUsername("%王%");
queryVo.setUser(user);
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession session = factory.openSession();
//4.创建Dao代理对象
UserDao userDao = session.getMapper(UserDao.class);
//5.执行方法
List<User> userByVo = userDao.findUserByVo(queryVo);
for (User user1 : userByVo) {
System.out.println(user1);
}
session.close();
in.close();
}
3.Mybatis结果类型的封装
1.ResultType输出类型
1.简单类型
2.pojo对象
pojo对象的话,要保证mapper.xml文件中属性名要和类中的属性保持一致;
Mybatis在封装数据成pojo对象的时候,需要注意两点:
1.pojo对象的属性名要和Mysql表中字段名保持一致
2.windows下的Mysql不区分大小写,因此pojo对象中属性名大小写都行;但是Linux中Mysql是严格区分大小写的,因此要保证pojo对象属性大小写也要和mysql保持一致。
以下为例:
<insert id="saveUser" parameterType="com.itheima.domain.User">
<!-- keyProperty 指的是实体类的属性
keyColumn 对应的是数据库表的
order 在插入后还是擦汗如前获取值
#{}中的参数是javaBean的属性名
-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{brithday});
</insert>
keyProperty 指的是javaBean的属性
keyColumn 对应的是数据库表
#{}中的参数是javaBean的属性名
Mysql字段和JavaBean属性对应不上的2种解决办法
如下图所示
方法1 起别名
原名为mysql字段,别名是JavaBean的属性
效率高,在sql上解决问题
方法2 配置属性和字段的映射
1.在dao.xml中的mapper标签下添加该类的映射配置:
2. sql配置中,resultType改成resultMap
这种方式,执行效率低一点;但是开发效率高一点
3.pojo列表
2.ResultMap结果类型
resultMap就是上面说的,字段和属性不一致通过映射配置解决;
P3 Mybatis自己写Dao实现类完成开发
1.自定义DaoImpl类开发流程
第一步:DaoImpl实现类,实现接口方法
方法中只做两件事: 1.通过factory创建SqlSession 2.用SqlSession对象执行方法(参数要找到配置文件中类+sql对应的id)
public class UserDaoImpl implements UserDao {
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory){
this.factory = factory;
}
@Override
public List<User> findAll() throws IOException {
//impl中 两件事:获取SqlSession, 执行sql获取查询结果
SqlSession sqlSession = factory.openSession();
List<User> users= sqlSession.selectList("com.itheima.dao.UserDao.findAll");
sqlSession.close();
return users;
}
第二步:dao.xml中配置信息
<mapper namespace="com.itheima.dao.UserDao">
<!-- 配置查询所有的接口对应的方法 和 SQL语句-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
</mapper>
第三步:主程序
主程序中到创建factory对象;
剩下的交给自定义实现类了
public void testFindAll() throws IOException{
//todo 2.自己实现Dao 前面写打到获取工厂对象即可!
//1.读取配置文件 配置文件中有连接数据库的信息
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2..创建SqlSessionFactory工厂 工厂用于生产操作对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.直接创建Dao实现类
UserDaoImpl userDao = new UserDaoImpl(factory);
//4.使用实现类执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
in.close();
}
2.保存操作
UserDaoImpl实现接口
@Override
public void saveUser(User user) {
SqlSession sqlSession = factory.openSession();
sqlSession.insert("com.itheima.dao.UserDao.saveUser",user);
sqlSession.commit();
sqlSession.close();
}
UserDao.xml配置该接口
<insert id="saveUser" parameterType="com.itheima.domain.User">
<!-- keyProperty 指的是实体类的属性
keyColumn 对应的是数据库表的
order 在插入后还是擦汗如前获取值
#{}中的参数是javaBean的属性名
-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
主程序
@Test
public void testSave() throws IOException {
User user = new User();
user.setUsername("jay");
user.setAddress("北京");
user.setSex("男");
user.setBirthday(new Date());
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
UserDaoImpl userDaoImpl = new UserDaoImpl(factory);
System.out.println(user);
userDaoImpl.saveUser(user);
System.out.println(user);
in.close();
}
User{id=null, username='jay', birthday=Mon Aug 23 00:35:11 CST 2021, sex='男', address='北京'}
User{id=49, username='jay', birthday=Mon Aug 23 00:35:11 CST 2021, sex='男', address='北京'}
3.修改和删除操作
UserDaoImpl实现接口
@Override
public void updateUser(User user) {
SqlSession sqlSession = factory.openSession();
sqlSession.update("com.itheima.dao.UserDao.updateUser",user);
sqlSession.commit();
sqlSession.close();
}
@Override
public void deleteUser(Integer id) {
SqlSession sqlSession = factory.openSession();
sqlSession.delete("com.itheima.dao.UserDao.deleteUser",id);
sqlSession.commit();
sqlSession.close();;
}
UserDao.xml配置该接口
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set username = #{username},address=#{address},sex=#{sex},birthday= #{birthday} where id = #{id};
</update>
<!-- dao方法中只有一个integer类型的参数,#{xxx}里面只需要写一个占位符即可,写什么都可以 -->
<delete id="deleteUser" parameterType="INTEGER">
delete from user where id = #{id};
</delete>
主程序
@Test
public void testUpdate() throws IOException {
User user = new User();
user.setId(46);
user.setUsername("Jay Chou");
user.setAddress("北京");
user.setSex("男");
user.setBirthday(new Date());
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDaoImpl userDao = new UserDaoImpl(factory);
userDao.updateUser(user);
in.close();
}
@Test
public void testDelete() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDaoImpl userDao = new UserDaoImpl(factory);
//5.执行方法
userDao.deleteUser(49);
in.close();
}
4.查询一个\模糊查询\聚合查询
UserDaoImpl实现接口
@Override
public User findById(Integer id) {
SqlSession sqlSession = factory.openSession();
User user = sqlSession.selectOne("com.itheima.dao.UserDao.findById", id);
sqlSession.close();
return user;
}
@Override
public List<User> findByName(String username) {
SqlSession sqlSession = factory.openSession();
List<User> users = sqlSession.selectList("com.itheima.dao.UserDao.findByName", username);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
return users;
}
@Override
public int findTotal() {
SqlSession sqlSession = factory.openSession();
Integer i = sqlSession.selectOne("com.itheima.dao.UserDao.findTotal");
sqlSession.close();
return i;
}
UserDao.xml配置该接口
<!-- 根据id查询一个用户-->
<select id="findById" parameterType="INTEGER" resultType="com.itheima.domain.User">
select * from user where id = #{id};
</select>
<!-- 根据id查询一个用户-->
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username like #{name};
</select>
<!-- 查询总记录数 -->
<select id="findTotal" resultType="int">
select count(id) from user;
</select>
主程序
@Test
public void testFindById() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDaoImpl userDao = new UserDaoImpl(factory);
User user = userDao.findById(46);
System.out.println(user);
in.close();
}
@Test
public void testFindByName() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDaoImpl userDao = new UserDaoImpl(factory);
//5.执行方法 注意提供百分号 这里
List<User> users = userDao.findByName("%王%");
for (User s : users) {
System.out.println(s);
}
in.close();
}
@Test
public void testCount() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件创建FactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
UserDaoImpl userDao = new UserDaoImpl(factory);
//5.执行方法 注意提供百分号 这里
int total = userDao.findTotal();
System.out.println(total);
in.close();
}
查增删改方法执行过程分析
(1)增删改
DefaultSqlSession类中insert\update\delete代码片段:
public int insert(String statement) {
return this.insert(statement, (Object)null);
}
public int insert(String statement, Object parameter) {
return this.update(statement, parameter);
}
public int update(String statement) {
return this.update(statement, (Object)null);
}
public int update(String statement, Object parameter) {
int var4;
try {
this.dirty = true;
MappedStatement ms = this.configuration.getMappedStatement(statement);
var4 = this.executor.update(ms, this.wrapCollection(parameter));
} catch (Exception var8) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);
} finally {
ErrorContext.instance().reset();
}
return var4;
}
public int delete(String statement) {
return this.update(statement, (Object)null);
}
public int delete(String statement, Object parameter) {
return this.update(statement, parameter);
}
也就是增删改对应最后都是执行public int update(String statement, Object parameter)
方法
update都是由delegate调用的
delegate是BaseExecutor类
最后会发现,执行的也是PreparedStatement的execute方法
(2)总体流程图
Mybatis使用代理dao的执行过程
关注如何代理(第三个参数)
实现了invocationHandler接口
执行被代理对象的任何方法的时候,都会经过这个invoke方法
可以看到invoke方法中,调用了我们在dao中定义的方法,所以我们不需要写dao实现类了
Mybatis配置文件中一些标签的属性
<properties>标签
1. resource属性
支持将连接信息配置到配置文件中,通过resource标签引入此文件;
此文件位置必须在类路径下(resourcesu目录下可)
注意:配置后,value的值要和配置文件中的key保持一致
2. url属性
url属性也可以指定配置文件位置;
用url指定,可以不限制于类路径下;
URL和URI的区别
<typleAliases>标签
mybatis配置文件中,这个标签用于给domain类中类起别名的;
当起了别名,用别名就不区分大小写;
类起别名
<package>标签
此标签是typeAliases的子标签;
配置后,此包下的所有类不用写全限定名了,类名就是别名