MyBatis入门
为何要学习MyBatis?
原始JDBC编程存在的问题
1)代码繁琐,重复
2)资源频繁创建和销毁(已经使用连接池解决)
3)sql语句写死在代码中,不方便维护
4)?占位符参数设置当参数较多时比较麻烦
5)封装处理结果集很麻烦
如图:
1.png要解决这个问题可以通过今天我们要学习的MyBatis技术 ,那么为何MyBatis技术能解决,这要归功于它属于ORM框架。
要搞清楚什么是ORM框架,我们先要弄清楚什么是框架
什么是框架,框架从何而来,为什么使用框架?
框架
1.是一系列jar包,其本质是对JDK功能的拓展.
2.框架是一组程序的集合,包含了一系列的最佳实践,作用是解决某一个领域的问题.
如图:
3.png最佳实践(Best Practice):实际上是无数程序员经历过无数次尝试之后,总结出来的处理特定问题的特定方法.
如果把程序员的自由发挥看作是一条通往成功的途径,最佳实践就是其中的最短路径,能极大的解放生产力.
Web开发中的最佳实践:分层开发模式(技术层面的"分而治之")
JavaEE开发根据职责的纵向划分:表现层,业务层,持久层:
表现层(Predentation Layer):web/mvc: 负责处理与界面交互的相关操作 (Struts2/Spring MVC)
业务层(Business Layer) :service: 负责复杂的业务逻辑计算和判断 (Spring)
持久层(Persistent Layer) :dao: 负责将业务逻辑数据进行持久化存储 (MyBatis/Hibernate)
持久层封装了数据访问的细节,为业务逻辑层提供了面向对象的API.完善的持久层应该达到:
1.代码重用性高,可以完成所有的数据访问操作.
2.具有相对独立性,当持久层变化时,不会影响上一层实现.
OR Mapping思想
对象关系映射(Object Relational Mapping,简称ORM):
是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。
ORM 主要解决对象-关系的映射:
面向对象概念 面向关系概念
----------------------------
类 表
对象 表的行(记录)
属性 表的列(字段)
如图:
2.pngORM的实现思想:
将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
目前流行的ORM框架:
1.JPA:本身是一种ORM规范,不是ORM框架.由各大ORM框架提供实现.
2.Hibernate:目前最流行的ORM框架.设计灵巧,性能优秀,文档丰富.
3.MyBatis:本是apache的一个开源项目iBatis,提供的持久层框架包括SQL Maps和DAO,允许开发人员直接编写SQL等
什么是Mybatis?
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架,严格上说应该是一个SQL 映射框架。
MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Ordinary Java Objects,普通的 Java 对象)映射成数据库中的记录。
MyBatis的前身是iBatis,MyBatis在iBatis的基础上面,对代码结构进行了大量的重构和简化;
相关文档:
Mybatis入门/资料/文档/mybatis-3离线文档/logging.html
Mybatis的原理图
4.png基本的CRUD
MyBatis的准备工作
user数据库表和模型对象User
创建t_user 表,并初始化一些数据,创建项目,创建domain的包,创建User类
表t_user:
CREATE TABLE `t_user` (
`id` bigint(20) PRIMARY KEY AUTO_INCREMENT,
`name` varchar(20),
`salary` decimal(8,2),
`hiredate` date
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
5.png
MyBatis依赖jar包
1).核心包:mybatis-3.1.1/lib/*.jar
2).MySQL驱动包:mysql-connector-java-5.1.*.jar
主配置文件
XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。
6.png
映射文件
用来封装sql语句的,sql语句的类型是操作数据的增删改查。每条sql语句都被一个标签包裹着,该标签中定义的一个id属性,目的是用来区分的。所以必须保证在该文件中id的内容是不允许重复的。
7.png离线约束的配置
主配置文件的相关配置
1.创建xml文件
2.在参考文档中拷贝约束头
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
3.如果有网络,IDE工具会自动去访问网络地址获取到对应DTD约束文件,然后我们在写标签的时候,就会有自动提示,提高我们的开发效率
如果没有网络这么办???
我们可以通过关联本地的约束文件,这样就相当于配置一个离线的约束,效果是一样的。都会有提示信息
保存操作
学习思路以及开发步骤
查看文档 Mybatis入门/资料/文档/mybatis-3离线文档/index.html
a. sqlSession 是执行SQL语句的,获取的方式是通过SqlSessionFactory来获取:
SqlSession session = sqlSessionFactory.openSession();
b. 要想获取sqlSession,那么先要获取SqlSessionFactory对象,要获取工厂对象
通过构建器来获取,要构建工厂对象通过加载xml文件(文件中配置 事务, 四要素)
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
c. 编写构建器需要的xml文件 (mybatis-config.xml)
开发步骤:
1.获取到SqlSessionFactory对象,该对象相当于一个连接池对象
通过加载xml配置文件,将数据读取到内存中封装成一个个的对象,然后通过操作该对象去操作数据库
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2.从SqlSessionFactory对象中获取到SqlSession对象,该对象相当于一个连接对象
SqlSession session = sqlSessionFactory.openSession();
3.获取到需要执行的SQL,然后发送到数据库中执行即可
4.需要手动提交事务
5.释放资源
把连接还给连接池对象
注意:如果忘了提交事务,数据库中的数据不会改变
如果忘了释放资源,资源可能会被耗尽
获取自动生成主键
获取自动生成主键
在执行保存的时候,需要获取到刚刚在数据库中生成的主键
在mybatis中,如何实现呢?
日志文件和MyBatisUtil
日志文件
配置日志文件监控MyBatis的运行.
在持久层的开发过程中,我们程序员需要随时观察SQL的执行情况,如果sql有问题,我们能够及时发现
所以,如果能够在控制台中将我们执行的SQL全部打印出来,那么就可以方便的观察SQL的相关问题
日志级别:
ERROR > WARN > INFO > DEBUG > TRACE
如果设置级别为INFO,则优先级高于等于INFO级别(如:INFO、WARN、ERROR)的日志信息将可以被输出,小于该级别的如DEBUG和TRACE将不会被输出。
10.png抽取MyBatisUtil
在执行CRUD的过程中,每次都要重新获取SqlSessionFactory对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-cfg.xml"));
但是上面获取SqlSessionFactory对象的操作非常耗时和耗资源的,所以,在实际开发中,应该要将SqlSessionFactory的实现共享,不需要反复创建
所以,我们将这些操作抽取到一个工具类中即可,并且获取SqlSessionFactory对象的操作应该是一个静态代码块中完成。
代码如下:
public class MyBatisUtil {
private MyBatisUtil() {
}
private static SqlSessionFactory factory;
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession() {
return factory.openSession();
}
}
更新和删除操作
SQL中参数获取的方式:
1.如果我们传递的是一个引用类型的对象,此时SQL中#{属性名},根据属性名去对象中获取数据
此时#{}中的名称不能随便
2.如果传递的是简单类型的数据,此时获取参数不需要通过参数名称获取,仅仅是将传递过来的参数直接作为SQL需要的参数
此时#{}中的名称可以随便
User.java
@Setter
@Getter
@ToString
public class User {
private Long id;
private String name;
private BigDecimal salary;
private Date hiredate;
}
更新
IUserDAO.java
public interface IUserDAO {
/**
* 修改数据
* @param user 修改的内容
*/
void update(User user);
}
UserDAOImpl.java
public class UserDAOImpl implements IUserDAO {
@Override
public void update(User user) {
SqlSession session = MyBatisUtil.getSqlSession();
session.update("cn.wolfcode.day08.mybatis.mapper.UserMapper.update", user);
session.commit();
session.close();
}
}
UserMapper.xml
<update id="update">
update user
set
name= #{name},
salary=#{salary},
hiredate=#{hiredate}
where id = #{id}
</update>
UserDAOTest.java
public class UserDAOTest {
private IUserDAO userDAO = new UserDAOImpl();
@Test
public void testUpdate() {
User user = new User();
user.setHiredate(new Date());
user.setName("秋香");
user.setId(2L);
user.setSalary(new BigDecimal(14000));
userDAO.save(user);
}
}
删除
IUserDAO.java
public interface IUserDAO {
/**
* 删除数据
* @param id 删除的条件id
*
*/
void delete(Long id);
}
UserDAOImpl.java
public class UserDAOImpl implements IUserDAO {
@Override
public void delete(Long id) {
SqlSession session = MyBatisUtil.getSqlSession();
session.delete("cn.wolfcode.day08.mybatis.mapper.UserMapper.delete", id);
session.commit();
session.close();
}
}
UserMapper.xml
<delete id="delete">
delete from user where id= #{id}
</delete>
UserDAOTest.java
public class UserDAOTest {
private IUserDAO userDAO = new UserDAOImpl();
@Test
public void testDelete() {
userDAO.delete(3L);
}
}
查询操作
User.java
@Setter
@Getter
@ToString
public class User {
private Long id;
private String name;
private BigDecimal salary;
private Date hiredate;
}
查询单个
IUserDAO.java
public interface IUserDAO {
/**
* 查询单条记录
* @param id 查询的条件id
* @return 获取查询指定的结果
*/
User get(Long id);
}
UserDAOImpl.java
public class UserDAOImpl implements IUserDAO {
@Override
public User get(Long id) {
SqlSession session = MyBatisUtil.getSqlSession();
if (session == null) {
return null;
}
User user = session.selectOne("cn.wolfcode.day08.mybatis.mapper.UserMapper.get", id);
return user;
}
}
UserMapper.xml
<!--
resultType:返回的期望类型的类的全限定名或者别名
注意:如果返回的结果是集合类型,那应该是集合包含的类型,
而不是集合本身
-->
<select id="get" resultType="user">
select * from user where id = #{id}
</select>
UserDAOTest.java
public class UserDAOTest {
private IUserDAO userDAO = new UserDAOImpl();
@Test
public void testGet() {
User user = userDAO.get(1L);
System.out.println(user);
}
}
查询多个
IUserDAO.java
public interface IUserDAO {
/**
* 获取所有的记录
* @return 获取查询的所有结果
*/
List<User> listAll();
}
UserDAOImpl.java
public class UserDAOImpl implements IUserDAO {
@Override
public List<User> listAll() {
SqlSession session = MyBatisUtil.getSqlSession();
if (session == null) {
return null;
}
List<User> userList = session.selectList("cn.wolfcode.day08.mybatis.mapper.UserMapper.listAll");
return userList;
}
}
UserMapper.xml
<select id="listAll" resultType="user">
select * from user
</select>
UserDAOTest.java
public class UserDAOTest {
private IUserDAO userDAO = new UserDAOImpl();
@Test
public void testListAll() {
List<User> listAll = userDAO.listAll();
for (User user : listAll) {
System.out.println(user);
}
}
}
常见的错误总结
1.唯一标示拼写错误
error_1.png2.设置参数占位符 要使用#{}
error_2.png3.使用别名,没有在主配置文件中进行配置
error_3.png4.从配置文件中获取值使用#{}
error_4.png5.主配置文件加载mapper映射文件路径错误
error_5.png6.namespace的内容不能随意写
error_6.pngCRUD拓展
使用类型别名-typeAlias
在mapper映射文件中,查询的SQL需要指定数据的封装类型---类的全限定名(很长)
如果多次编写类的全限定名,如果出错,很麻烦
所以,如果为这个类设值一个别名之后,那么使用就方便
如果一个项目中有100个类需要设置别名,那么配置就很麻烦了---使用包的扫描即可
MyBatis 提供了 typeAliases标签
这个标签可以给类起别名
通过package标签可以扫描指定的包下面的所有javaBean,给javaBean起别名,别名就是类的名字
11.png
抽取db.properties
在主配置文件中,连接数据库的四要素写死在里面,不好维护,所以为了方便管理,把四要素信息抽取到db.properties配置文件中.
注意:
1.要把db.properties文件和主配置文件进行关联
通过<properties resource/>标签
2.获取db.properties中的值,是通过文件中的key
所以要保证${key} 和db.properties中的key 一致
12.png
结果映射-ResultMap
问题:在CRUD示例中,get和list方法,直接通过resultType=User来设置MyBatis把ResultSet中的每一行数据包装为User对象;
这样要求数据表的列名和对象的属性名要对应;
如果列名和对象属性名没法对应的时候,我们就需要额外的引入ResultMap
修改表中的列名,让列名和属性名不同
ResultMap的使用
<resultMap id="BaseResultMap" type="User" >
<id property="id" column="id" javaType="" jdbcType=""/>
<result property="name" column="name"/>
<result property="salary" column="salary"/>
<result property="hiredate" column="hiredate"/>
</resultMap>
<select id="get" parameterType="int" resultMap="BaseResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
resultType和resultMap的注意:什么时候用resultType?什么时候用resultMap?
当表中的字段名称和类中的属性名称一致的时候用resultType
当不一致的时候需要手动映射 用resultMap
ResultMap元素详解
ResultMap
定义了一个ORM的具体映射方式
1,type:代表O,即最终返回的对象类型
2,id:为该映射设置一个名称,这个名称就是在get或list中使用的resultMap对应的id
id/result
对应这属性的映射,可以参考hibernate的property。id和result的区别在于,id一般用于映射主键,可以提高速度,result一般对于普通的属性。
Eclipse插件-MyBatipse
建议在线安装:
方式一:在Help->Eclipse Marketplace中搜索MyBatipse安装即可
13.png方式二:离线安装
动态SQL
if
用于判断,一般用作是否应该包含某一个查询条件
如:
<select id="findActiveBlogWithTitleLike"
parameterType="Blog" resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
直接使用if元素的问题在于,if元素不能用于判断where关键字是否存在;
where
where元素:如果查询条件没有“WHERE“关键字,则自动在查询条件之前插入“WHERE“,如果查询条件以“AND”或“OR”开头,那么就会使用 WHERE 关键字替换
如:
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
AND state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
网友评论