一、什么是框架?
框架( Framework )是构成一类特定软件可复用设计的一组相互协作的类。框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节。
软件系统发展到今天已经很复杂了,特别是服务器端软件,涉及到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事务处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。
框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。
软件为什么要分层? 为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源…总之好处很多啦。
二、什么是ORM?
ORM:(Object/Relation Mapping): 对象/关系映射
ORM的实现思想:
将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
三、回顾JDBC连接数据库
步骤:
1.注册驱动,获取连接
2.创建Statement对象
3.execute()方法执行SQL
4.把结果集转换成POJO对象
5.关闭资源
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/tianliang";
String username = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, username, password);
String sql = " select * from t_user where; ";
PreparedStatement pst = conn.prepareStatement(sql);
ResultSet rs = pst.executeQuery();
while(rs.next()) {
int id = rs.getInt(1);
String uname = rs.getString(2);
String pw = rs.getString(3);
String nickname = rs.getString(4);
System.out.println(id+" "+uname+" "+pw+" "+nickname);
}
rs.close();
pst.close();
conn.close();
思考:当项目复杂时,直接使用原生的API会带来什么问题?
①、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
②、Sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java代码。
③、使用 preparedStatement 占位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能多也可能 少,修改 sql 还要修改代码,系统不易维护。
④、对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成 pojo 对象解析比较方便,没有实现实体类到数据库记录的映射。
⑤、没有提供缓存功能。
四、Mybatis相关介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持 SQL、存储过程以及高级映射。
如 JDBC中 我们需要有繁琐的流程,和设置参数,并且获取结果集的时候也需要循环遍历,并且手动封装到实体类对象中
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,
而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
MyBatis 可以使用 简单的 XML 或注解来配置和映射 接口和 Java 的 实体类
MyBatis 有两种用法,一个是注解,一个是XML,各有千秋
注解 : 使用注解的话,方便一些,不需要有XML配置文件,可能看上去会好看一些,但是需要代码和SQL在一起
XML : 把SQL语句放到XML文件中,java代码中会感觉干净一些,并且,使用XML形式 写一些复杂的SQL语句会比较方便,也能统一管理,有其他同事接手工作的时候,或者其他同事优化数据库查询的时候,位置好找,修改便捷,不容易出错
所以一般常用的就是XML,可以做到sql分离
有时候也是混用,简单的就用注解,复杂的就用XML
Mybatis特性:
1、使用连接池对链接进行管理
2、SQL和代码分离,几种管理
3、参数映射和动态SQL
4、结果集映射
5、缓存管理
6、重复SQL的提取 <sql>
7、插件机制
使用场景 :
操作持久化层(Dao),和JDBC一样,操作数据库的
当我们要写动态页面(jsp)和用户进行交互的时候,就需要操作数据库,
而MyBatis就是负责操作数据库的
五、Mybatis与Hibernate的区别
MyBatis :
1 入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
2 可以进行更为细致的SQL优化,可以减少查询字段。
3 缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
4 二级缓存机制不佳
整体 : 小巧、方便、高效、简单、直接(SQL操作)、半自动
Hibernate :
1 功能强大,数据库无关性好,对象关系(O/R)映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。
2 有更好的二级缓存机制,可以使用第三方缓存
3 缺点就是学习门槛不低,要精通门槛更高,而且怎么设计对象关系(O/R)映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行
形象举例 :
mybatis:机械工具,使用方便,拿来就用,但工作还是要自己来作,不过工具是活的,怎么使用,由我决定。
hibernate:智能机器人,但研发它(学习、熟练度)的成本很高,工作都可以拜托给他了,但仅限于它能做的事
六、Mybatis环境搭建及API使用
中文官网 : mybatis – MyBatis 3 | 入门
6.1.创建要操作的数据表
create table t_user(
id int(11) primary key auto_increment,
username varchar(100),
password varchar(100),
nickname varchar(100),
`type` int (2)
);
create table t_address(
id int(11) primary key auto_increment,
addr varchar(255),
phone varchar(100),
postcode varchar(100),
user_id int(11),
);
insert into t_user(username,password,nickname,type) values('admin','123','超级管理员',1);
insert into t_address(addr,phone,postcode,user_id) values ('123','123','123',1);
6.2.创建一个java项目
6.3导入需要的jar包
6.4.创建User和Address实体类
public class User {
private int id;
private String username;
private String password;
private String nickname;
// 1 是管理员,0是普通用户
private int type;
/**
* 地址是多,User是一
*/
private List<Address> addresses;
}
public class Address {
private int id;
private String phone;
private String addr;
/**
* 邮政编码
*/
private String postcode;
/**
* 保存User对象,是一个双向关联
*/
private User user;
}
6.5.在src下创建数据源信息文件 jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/ssm
username=root
password=root
6.6.在src下创建控制打印日志的 log4j.properties
log4j.rootLogger=DEBUG, stdout
log4j.logger.com.ibatis = DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource = DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner = DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate = DEBUG
log4j.logger.java.sql.Connection = DEBUG
log4j.logger.java.sql.Statement = DEBUG
log4j.logger.java.sql.PreparedStatement = DEBUG
log4j.logger.java.sql.ResultSet = DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
6.7.在src下创建mybatis配置文件 mybatis-config.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>
<!-- 指定数据源文件位置,获取数据源数据信息 -->
<properties resource="jdbc.properties"></properties>
<!-- 设置别名,然后就可以使用别名来代替前面的路径 -->
<typeAliases>
<typeAlias type="com.tledu.mjw.model.User" alias="User"/>
</typeAliases>
<!--
开发环境有,开发、测试、运行环境,一般会设置两个environment
一个是用户开发环境,使用id为development
一个是正式环境使用id为final,那么通过配置,environments的default
属性就能选择使用对应的environment了
-->
<environments default="development">
<environment id="development">
<!--声明使用jdbc方式来提交和回滚事务,依赖于从数据源得到的连接来管理事务范围。
JDBC使用 数据库自己的事务(局部事务),connect.beginTranstion(), connect.commit()等
JTA : 使用jta 事务管理器管理事务(全局事务),使用userTranstion对象
External: 自己(Ibatis)不控制事务,事务交由外部控制,一般在CTM,或spring托管事务中使用-->
<transactionManager type="JDBC" />
<!--1.UNPOOLED:不使用连接池的数据源
2.POOLED:使用连接池的数据源
3.JNDI:使用JNDI实现的数据源 具体使用细节可问度娘或是谷大师-->
<dataSource type="POOLED">
<!-- ${} 在jsp中是EL表达式,这里是OGNL表达式 -->
<!-- 自动获取jdbc.properties里面的值,设置到连接池中
name="driver" 这个driver不能更改
value="${driver}" 这个driver需要和jdbc.properties中一致
-->
<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/tledu/mjw/mapper/UserMapper.xml"/>
</mappers>
</configuration>
6.8.创建UserMapper.xml SQL映射文件
<?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">
<!--
namespace 是用于调用的时候的映射
-->
<mapper namespace="User">
<!--
id : 表示调用的SQL的名字,相当于方法名
parameterType : 表示传入参数的类型,写类全名,但是由于设置的别名,所以可以写User
resultType : 结果集类型
-->
<insert id="add" parameterType="User" >
<!-- 这里的#username 就等于是用 ? 的方式,等方法调用的时候,会传递一个参数,就会自动映射到username的属性上 -->
insert into t_user (username,password,nickname,type) values (#{username},#{password},#{nickname},#{type})
</insert>
</mapper>
6.9.加入Junit单元测试
6.10.Junit的使用
/**
* @Test 对应的方法只能是成员方法,相当于main()方法一样,会被自动调用执行
*
* @Before 对应的方法只能是成员方法,在@Test方法执行之前执行
* @After 对应的方法只能是成员方法,在@Test方法执行之后执行
并且 以上两个都是有多少个 @Test 就执行多少次
*
* @BeforeClass 对应的方法只能是静态方法,在执行第一个 @Test 之前执行,并且只执行一次,在 @Befor 之前执行
* @AfterClass 同上,在最后执行,并在 @After 之后
*
* 我们可以把测试代码放到 @Test 中,测试代码需要准备的数据或措施放到 @Before 和 @After 中,比如开始事务和事务提交
* 把全局需要的数据和措施放到 @BeforeClass 和 @AfterClass 中 比如创建连接和关闭链接
*/
@Before
public void B() {
System.out.println("Before");
}
@After
public void A() {
System.out.println("After");
}
@BeforeClass
public static void BS() {
System.out.println("BeforeClass");
}
@AfterClass
public static void AS() {
System.out.println("AfterClass");
}
6.11.测试Mybatis
@Test
public void testAddUser() throws IOException {
String resource = "mybatis-config.xml";
// 读取总配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 获取session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 调用xml中的sql
User user = new User();
user.setUsername("1234");
user.setPassword("1234");
user.setNickname("1234");
user.setType(0);
// 调用
session.insert("User.add", user);
// 提交
session.commit();
}
七、Mapper的三种形式
第一种 : sqlSession执行对应语句
第一种 : sqlSession执行对应语句,就是我们上面测试的入门案例
只需要创建对应的Mapper xml,然后在mybatis-config.xml中映射即可
第二种 : 使用注解(基本不用)
1.需要创建接口
2.创建与接口名对应的mapper.xml文件
3.在mybatis-config.xml中映射创建的接口
4.测试方式同上 session.insert("User.add",user);
调用
session.getMapper(IUserDaoMapper.class).add(user); session.insert("com.tledu.mjw.dao.IUserDaoMapper.add",user); 这两种提交方式都可以,选其一 session.commit();
第三种 : 利用接口代理(常用)
第三种:接口代理
1 需要mapper.xml
2 接口中不需要添加对应的注解
3 mybatis-config.xml中添加映射配置
4.调用与第一种一样的方式
调用
session.getMapper(IUserDaoMapper.class).add(user); session.insert("com.tledu.mjw.dao.IUserDaoMapper.add",user); 这两种提交方式都可以,选其一 session.commit();
八、Util工具类的封装
以上代码中,红框部分我们每次都需要重复编写,mybatis-config.xml在整个生命周期只需要加载一次就行,而SqlSessionFactory也只需要创建一个,所以 我们把这些封装成方法
public class MyBatisUtil {
private MyBatisUtil() {}
private static SqlSessionFactory sessionFactory = null;
static {
String resource = "mybatis-config.xml";
// 读取总配置文件
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
// 获取session工厂
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static SqlSession getSession() {
return sessionFactory.openSession();
}
public static void closeSession(SqlSession session) {
if (session != null) {
session.close();
}
}
}
总结 :
使用工具类,能够降低我们dao的代码量,并且也能更好的优化代码,比如关闭资源,和创建资源(能够保证某些资源在生命周期中只会被创建一次) 工具类,使用方便,复用价值高
关于使用Mybatis时SqlSession的报错:Excutor was closed的解决办法。
由于sqlsession是非线程安全的,因此在做sqlSession工具类的时候请将SqlSession置于方法体中,不要使用与全局变量中。
九、CRUD实操
上面我们已经对MyBatis的环境搭建和工具类优化以及三种使用方式有所了解
上面我们只是以添加为例来进行的测试,下面我们就来编写一下增删改查
Namespace先用User了,这里我们使用SqlSession中原生的方法是那些
UserMapper.xml
<mapper namespace="User">
<insert id="add" parameterType="User">
insert into t_user
(username,password,nickname,type) values
(#{username},#{password},#{nickname},#{type})
</insert>
<delete id="delete" parameterType="int">
delete from t_user where id =#{id}
</delete>
<update id="update" parameterType="User">
update t_user set password =
#{password} , nickname=#{nickname} ,type=#{type} where id = #{id}
</update>
<select id="load" parameterType="int" resultType="User">
select * from t_user where id = #{id}
</select>
</mapper>
UserDao实现类
添加
@Override
public void add(User user) {
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
session.insert("User.add", user);
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSession(session);
}
}
删除
@Override
public void delete(int id) {
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
session.delete("User.delete", id);
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSession(session);
}
}
更新
@Override
public void update(User user) {
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
session.update("User.update", user);
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSession(session);
}
}
查询
@Override
public User select(int id) {
SqlSession session = null;
User user = null;
try {
session = MyBatisUtil.getSession();
user = session.selectOne("User.load", id);
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSession(session);
}
return user;
}