文章目录
1 Mybatis概述
1.1 认识框架
什么是框架
框架就是一个架子,表演节目,舞台已经搭建好,表演什么节目,看自己的需求了。
框架是一个半成品,对于Java语言来说,框架就是封装了别人的代码。在框架的基础上我们在进一步开发,拿来主义。
框架解决什么问题
解决的是技术整合问题。软件开发环境和规模都很大,不可能任何一个项目的代码都从零开始,此时就需要一个非常优秀的框架把基础技术整合完毕,我们在他的基础上进一步开发。提高性能,易扩展,易维护,最终提高整个团队的开发效率。
什么时候使用框架
企业级大型项目开发
怎么使用框架
Java的框架是具有一些共性
- 导入jar包
- 框架运行细节定义,也就是编写配置文件(xml)
- 调用框架中的api
1.2 MyBatis框架
Mybatis原本是Apache软件基金会的一个开源项目叫做iBatis,2010年这个项目由Apache迁移到了google code管理才改名为Mybatis,2013年又迁移到了GitHub。
Mybatis是一个优秀的持久层框架(Dao层框架),它是对JDBC的封装,使得开发者只需要关注Sql语句(业务本身)即可,无需开发者处理加载驱动、获取连接、创建Statement等繁琐的过程。
Mybatis最大的特点是把Sql语句写在XML配置文件当中。而且Mybatis执行完Sql语句之后可以以对象形式返回(POJO/POJO集合等)。
Mybatis是一个实现了ORM思想的持久层框架。
ORM:Object/Relation Mapping 对象/关系映射。
ORM思想:将数据库中的关系数据表映射为JAVA中的对象,把对数据表的操作转换为对对象的操作,实现面向对象编程。因此ORM的目的是使得开发人员以面向对象的思想来操作数据库。
比如:原来insert使用的是insert into…,如果使用实现了ORM思想的持久层框架,就可以在Java程序中直接调用api,比如insert(User),达到操作对象即操作数据库的效果。Hibernate框架是一个全自动的ORM持久层框架,只需要编写POJO,在xml中定义好Pojo属性和数据表字段的映射/对应关系,就可以在java中实现类似 insert(User)的操作。Sql语句都不用写。但是因为性能等问题,市场占有率越来越低
Mybatis框架是一个半自动的ORM持久层框架,也可以在Java中实现类似 insert(User)的操作最终操作数据库,但是需要我们自己写Sql语句。Mybatis是目前比较流行的Dao层框架。
1.3 MyBatis快速入门
原生的JDBC案例
* 原始的JDBC,对数据表user查询,结果集存储List集合** 原始程序的问题: 维护性差,扩展性差,出现问题,修改源码* 代码量大* 频繁连接,释放数据库资源,降低系统性能* 解决办法: 引出连接池,Apache DBUtils** 数据库驱动类 ,连接四大信息* 解决办法: 写配置文件,读取** SQL语句硬编码,SQL语句写死了* SQL语句中的?占位符* 解决办法:写配置文件,读取** 封装数据表结果集代码,硬编码** 框架: 最基本的代码,整合起来* 连接数据库,执行SQL语句,封装结果集整合* 只要提供: SQL语句,写xml中
publicclassJdbcDemo{publicstaticvoidmain(String[] args)throws Exception{//注册驱动程序
Class.forName("com.mysql.jdbc.Driver");//获取连接
Connection con=
DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis","root","root");//SQL语句执行对象
PreparedStatement pst= con.prepareStatement("select * from user ");//执行查询,返回结果集
ResultSet rs= pst.executeQuery();
List<User> userList=newArrayList<User>();while(rs.next()){
User user=newUser();//取出表中的数据, rs对象的方法getXXX()
user.setId( rs.getInt("id"));
user.setUsername( rs.getString("username"));
user.setSex(rs.getString("sex"));
user.setBirthday( rs.getDate("birthday"));
user.setAddress( rs.getString("address"));
userList.add(user);}for(User user: userList){
System.out.println(user);}
rs.close();
pst.close();
con.close();}}
使用MyBatis框架
- 所有的Dao层框架都是以接口的形式给我们提供增删改查的API
- 我们今天自定义Mybatis框架只完成一个API接口:selectList
* MyBatis框架的快速入门
* SqlMapConfig.xml 配置的是连接信息
* UserMapper.xml 配置的是数据表user的SQL语句
SqlMapConfig.xml:(配置了连接信息、事务、连接池、映射数据库表操作的配置文件UserMapper.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><!--
environments 运行环境,可以配置多个
default="development" 默认,值=development 开发环境
environment 运行环境,配置一个
id="development" id唯一性的属性 值=id="development" 开发环境
id="product" 生产环境
id="test" 测试环境
--><environmentsdefault="development"><environmentid="development"><!--
transactionManager 事务管理配置
type="JDBC" 使用的是最原始的JDBC的事务处理机制
type="MANAGERED" 不管理事务
--><transactionManagertype="JDBC"/><!--
dataSource 配置数据源,连接池
type="POOLED" 使用连接池
MyBatis自带连接池 (type=""UNPOOLED)
--><dataSourcetype="POOLED"><!--配置的是,数据库连接四大信息--><propertyname="driver"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment><environmentid="produce"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://127.0.0.1:3306/mybatisProduce?characterEncoding=utf8"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment><environmentid="test"><transactionManagertype="JDBC"/><dataSourcetype="POOLED"><propertyname="driver"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://127.0.0.1:3306/mybatisTest?characterEncoding=utf8"/><propertyname="username"value="root"/><propertyname="password"value="root"/></dataSource></environment></environments><!--
配置的是映射关系 ORM Java对象和数据表
--><mappers><!--
单独的映射关系
resource资源,映射文件的路径
UserMapper.xml 配置的是数据表user的SQL语句
--><mapperresource="UserMapper.xml"/><!--<mapper resource="StudentMapper.xml"/>--></mappers></configuration>
UserMapper.xml:(配置了查询语句)
案例:
* 需求: 查询user表,数据存储到List集合
* 实现步骤:
* 1:创建对象 SqlSessionFactoryBuilder
* 工厂的构建者对象,作用创建SQLSession对象工厂的
* 读取配置文件
*
* 2: SqlSessionFactory工厂创建
* SqlSession接口实现类对象
*
* 3: SqlSession接口实现类对象
* 调用方法select 查询数据表
*
* 4: 输出查询结果集
*
* 5: 释放资源
publicvoidmyBatisQuickStart(){//1:创建对象 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=newSqlSessionFactoryBuilder();//使用sqlSessionFactoryBuilder方法 build(),创建出SqlSessionFactory对象//build方法,读取配置文件,返回SqlSessionFactory对象,需要传递流对象,流对象绑定数据库配置文件
InputStream inputStream= QuickStart.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");// 2: SqlSessionFactory工厂创建
SqlSessionFactory sqlSessionFactory= sqlSessionFactoryBuilder.build(inputStream);//工厂对象,获取SqlSession接口实现类对象,工厂对象的方法 openSession
SqlSession sqlSession= sqlSessionFactory.openSession();//3: SqlSession接口实现类对象,调用方法 select 查询数据表//selectList方法参数不是SQL语句, namespace+"."+id 锁定SQL语句 test.queryList
List<User> userList= sqlSession.selectList("test.queryList");for(User user:userList){
System.out.println(user);}
sqlSession.close();}
1.4 Java中的日志组件(扩展)
#log4j日志级别如下:
#A:off 最高等级,用于关闭所有日志记录。
#B:fatal 指出每个严重的错误事件将会导致应用程序的退出。
#C:error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
#D:warn 表明会出现潜在的错误情形。
#E:info 一般和在粗粒度级别上,强调应用程序的运行全程。
#F:debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
#G:all 最低等级,用于打开所有日志记录。
#但log4j只建议使用4个级别,优先级从高到低分别是:
#error>warn>info>debug
log4j.rootLogger =debug,systemOut(日志输出到控制台),logFile(日志输出到文件)
#输出到控制台
log4j.appender.systemOut = org.apache.log4j.ConsoleAppender
log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout
log4j.appender.systemOut.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n
log4j.appender.systemOut.Target = System.out
#输出到文件
log4j.appender.logFile = org.apache.log4j.FileAppender
log4j.appender.logFile.layout = org.apache.log4j.PatternLayout
log4j.appender.logFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n
log4j.appender.logFile.File = E:/log/log4j.log
log4j.appender.logFile.Encoding = UTF-8
#将日志输记录到MySQL数据库
#log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender
#log4j.appender.logDB.layout = org.apache.log4j.PatternLayout
#log4j.appender.logDB.Driver = com.mysql.jdbc.Driver
#log4j.appender.logDB.URL = jdbc:mysql://localhost:3306/log4j?characterEncoding=utf-8
#log4j.appender.logDB.User = root
#log4j.appender.logDB.Password = root
#log4j.appender.logDB.Sql = INSERT INTO t_log4j(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)values('mybatis','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
2 MyBatis框架的CRUD
2.1 查询
SqlSession对象方法selectList(见快速入门)
sqlSession对象方法selectOne
<mapper namespace="test"><!--
配置,主键查询的SQL语句 ,标签select
test+.+id 锁定唯一SQL语句,具有唯一性
查询是有结果集
标签中属性 resultType:配置结果集的类型
SQL语句有参数的
标签中属性 parameterType: SQL语句的参数类型
SQL语句中的取参数语法#{基本类型 任意命名}
--><select id="queryUserById" resultType="com.itheima.pojo.User" parameterType="Integer"><!--
SQL语句
MyBatis配置SQL语句,参数不能写问号
取参数#{参数名}
参数名,如果只有一个参数,基本类型及其包装类和String,任意
-->select * from user whereid=#{id}</select><!--select 标签,配置的是查询语句id 属性,唯一性,在当前文件中唯一 ,属性值 queryList
框架执行SQL语句: namespace+"."+queryList 确定SQL语句
resultType: 查询的结果集,是pojo对象
标签体: 是SQL语句
--><select id="queryList" resultType="com.itheima.pojo.User">select * from user</select></mapper>
@Test/**
* 框架:查询数据表user,根据主键查询
*/publicvoidtestQueryUserById()throws IOException{/*//1:SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//MyBatis框架提供类, Resources静态方法 getResourceAsStream() 底层封装的就是类加载器,中的流
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");//自动从src源码目录下读取文件
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//2:创建 SqlSession接口实现类对象*/
SqlSession sqlSession= sqlSessionFactory.openSession();//3:方法,执行SQL语句//selectOne(namespace+"."+id 锁定唯一SQL, SQL语句的实际参数)方法,查询结果一个对象
User user= sqlSession.selectOne("test.queryUserById",3);
System.out.println(user);
sqlSession.close();}
2.2 关于模糊查询
select * from user where username like#{value}
orselect * from user where username like${value}
#{} ${} 获取参数的区别
Tomcat编译过程的差异:
select * from user where username like#{value}
参数编译为 ?select * from user where username like ?
传递的实际参数"'%王%'" ,替换到?占位符
原理:框架底层使用的JDBC中的接口 PreparedStatement,使用PreparedStatement为占位符?赋值,而不是字符串的拼接select * from user where username like${value}
直接拼接了字符串!!select * from user where username like'%王%'
推荐使用#{},即占位符的方式,效率更高
${}适合于特殊场景, SQL语句中的参数是 列名或者表名的时候select * from user order by 列名
2.3 插入
配置新增数据,标签insert
新增数据,SQL语句中,一定会有很多参数
参数封装在pojo对象,配置的时候为标签属性parameterType
SQL语句中参数的必须传递pojo对象中的属性名
增删改,结果是行数,int类型,不要配置resultType
新增操作:insert标签中,子标签<selectKey> 获取上一次新增主键值
属性:
order="after或者before" 获取主键,是在SQL语句之前,还是之后执行
resultType 获取主键的结果数据类型
keyProperty 属性,查询后的值存放:pojo对象的属性中
<insertid="saveUser"parameterType="com.itheima.pojo.User"><!--
传递的参数是对象 pojo
#{pojo对象的属性名!!}
-->
insert into user values (null,#{username},#{sex},#{birthday},#{address})<selectKeyorder="AFTER"resultType="Integer"keyProperty="id">
SELECT LAST_INSERT_ID ()</selectKey></insert>
/**
* 实现数据的新增。SQL语句参数,封装到pojo对象中
* SQLSession接口方法 insert
* 注意: 新增,修改,删除,动了数据表的数据 (事务)
* 并没有提交事务 sqlSession.commit();//提交事务
*/publicvoidtestSaveUser(){
SqlSession sqlSession= sqlSessionFactory.openSession();
User user=newUser();
user.setUsername("孙尚香");
user.setSex("女");
user.setAddress("东吴");
user.setBirthday(newDate());int row= sqlSession.insert("test.saveUser",user);
sqlSession.commit();//提交事务
System.out.println(row);
System.out.println(user);
sqlSession.close();}
2.4 更新
<!--
配置更新数据,标签update
--><updateid="updateUserById"parameterType="com.itheima.pojo.User">
update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}</update>
@Test/**
* 实现更新数据
*/publicvoidtestUpdateUserById(){
SqlSession sqlSession= sqlSessionFactory.openSession();
User user=newUser();
user.setId(8);
user.setUsername("孙小妹");
user.setSex("男");
user.setBirthday(newDate(0));
user.setAddress("益州");int row= sqlSession.update("test.updateUserById",user);
sqlSession.commit();
sqlSession.close();
System.out.println(row);}
2.5 删除
<!--
配置删除数据,标签delete
--><deleteid="deleteUserById"parameterType="Integer">
delete from user where id = #{id}</delete>
/**
* 实现数据删除
*/publicvoidtestDeleteUserById(){
SqlSession sqlSession= sqlSessionFactory.openSession();int row= sqlSession.delete("test.deleteUserById",8);
sqlSession.commit();
sqlSession.close();
System.out.println(row);}
2.6 关于事务(原始dao层开发)
在三层架构中,我们的事务处理都是放在service层处理的。在Mybatis框架中,我们使用SqlSession 对象来执行sql语句,执行事务。但是事务是应该放到service层处理的。
处理方法:
业务层建立SqlSession 对象,将SqlSession参数传递到dao层,处理sql语句。业务层SqlSession提交事务
例子:
service层:
/**
* 本类看成业务层
* 调用dao层,获取查询的结果
* 获取SqlSession对象,传递到业务层
*/publicvoidtestDao(){
SqlSession sqlSession= sqlSessionFactory.openSession();
UserDao userDao=newUserDaoImpl(sqlSession);
List<User> userList= userDao.queryUser();for(User user: userList){
System.out.println(user);}
sqlSession.close();}
dao层:
publicclassUserDaoImplimplementsUserDao{private SqlSession sqlSession;publicUserDaoImpl(SqlSession sqlSession){this.sqlSession= sqlSession;}@Override/**
* 重写接口的方法,查询数据库
* 需要的对象 SqlSession
* 业务层传递的,不应该自己获取(事务安全性)
*/public List<User>queryUser(){
List<User> userList= sqlSession.selectList("test.queryList");return userList;}}
2.7 关于代码优化
在MyBatis中,我们操作数据库的过程分为三步
* 1:SqlSessionFactoryBuilder
* 读取配置文件,创建出现 SqlSessionFactory
* 方法build(输入流) 只需要绑定主文件配置文件
*
* 2: SqlSessionFactory 工厂对象
* 创建 SqlSession接口实现类对象
*
* 3: SqlSession接口实现类对象
* 方法,执行SQL语句
每次执行CRUD,都需要绑定配置文件,创建SqlSession对象,这样代码的重复度很高。
解决办法:将绑定配置文件作为每次执行前的固定方法提出来
将SqlSession对象作为类的成员属性
private SqlSessionFactory sqlSessionFactory;/**
* junit注解@before,在每次执行@Test修饰方法之前都会执行before方法
*/@Beforepublicvoidbefore()throws IOException{
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=newSqlSessionFactoryBuilder();
InputStream inputStream= Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory= sqlSessionFactoryBuilder.build(inputStream);}
3 动态代理开发
3.1 原始dao层开发方式
- 实现步骤
- 定义dao层接口
- 定义dao层接口实现类
传统dao开发方式中的实现类其实起了一个连接、承上启下的作用,连接了接口和xml映射文件,效果就是调用接口方法时能够找到xml映射文件。
3.2 动态代理开发快速入门
定义一个Mapper接口,这