1. 计划
1. 介绍持久层框架Mybatis
2. JDBC和hibernate回顾
3. MyBatis入门
基础操作增删改查
基于接口实现增删改查
4. 核心配置文件讲解
2. MyBatis介绍
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
3. 操作数据库的方式回顾
3.1. 回顾JDBC
3.1.1. 简单JDBC例子
static{ //加载JDBC数据库驱动 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundExceptione) { e.printStackTrace(); } }
/*** * JDBC增加数据流程 * @throws SQLException */ public void select(){ Connection connection = null; ResultSet result = null; PreparedStatement statement =null; try { //创建数据库链接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8","root","123456");
//创建statment对象 String sql = "SELECT * FROM user WHERE id=? And username =?"; statement = connection.prepareStatement(sql); statement.setInt(1, 11); statement.setString(2,”小红思密达”);
//执行查询获得返回结果集 result = statement.executeQuery();
//从结果集里取数据 while (result.next()) { intid =result.getInt("id"); String name = result.getString("name"); intage =result.getInt("age"); System.out.println("id:" +id +",name:" +name +",age:" +age);
T user = … User.set null -àNullPointerException User.set }
} catch (Exceptione) { e.printStackTrace(); }finally{ //关闭资源 try { if (result !=null) { result.close(); } if (statement !=null) { statement.close(); } if (connection !=null) { connection.close(); } } catch (SQLExceptione) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
3.1.2. JDBC缺点
1. 频繁创建和打开、关闭数据连接,太消耗资源
2. Sql语句存在硬编码,不利于维护
3. Sql参数设置硬编码,不利于维护
4. 结果集获取与遍历复杂,存在硬编码,不利于维护,期望能够查询后返回一个java对象
3.2. Hibernate操作数据回顾
下面是hibernate执行增删改查的增加流程。
// 实例化配置对象加载映射文件加载 hibernate.cfg.xml Configuration configuration =newConfiguration().configure(); // 创建会话工厂 SessionFactory sessionFactory =configuration.buildSessionFactory();
// 创建会话 Session session = sessionFactory.openSession(); // 开启事务 Transaction transaction =session.beginTransaction(); // 编写自己的逻辑代码 Customer customer = new Customer(); customer.setName("小红"); customer.setAge(40); customer.setCity("北京"); // 直接保存---面向对象 session.save(customer);
// 提交事务 transaction.commit();
//关闭资源 session.close(); sessionFactory.close(); |
3.2.1. hibernate缺点
Ø SQL优化方面
Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。
Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。
Ø JDBC
Hibernate是在JDBC上进行了一次封装,相比原生SQL性能要低些。
Ø 学习成本
Hibernate提供了诸多功能和特性。要全掌握很难。
Ø 动态SQL
Hibernate不支持动态SQL
面试题话术总结
4. MyBatis入门
4.1. MyBatis入门案例
搭建MyBatis流程
1、导包—Maven 2、创建核心配置文件 3、创建SqlSessionFactory会话工厂 4、创建SqlSession会话 5、配置映射文件信息Mapper.xml----SQL 6、通过SqlSession实现增删改查 7、提交事务 8、关闭资源 |
4.1.1. 案例表结构
user表机构
CREATE TABLE `user` ( `userid` int(10) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `userage` int(10) NOT NULL, PRIMARY KEY (`userid`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; |
4.1.2. 创建mybatis.xml
XML 配置文件包含对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源和 决定事务范围和控制的事务管理器。
<?xmlversion="1.0"encoding="UTF-8"?> <!DOCTYPEconfiguration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> <!-- MyBatis数据源环境配置,这里的default指定的数据源环境必须存在 --> <environmentsdefault="development"> <!-- 数据源环境配置 --> <environmentid="development"> <transactionManagertype="JDBC"/> <dataSourcetype="POOLED"> <propertyname="driver"value="com.mysql.jdbc.Driver"/> <propertyname="url"value="jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8"/> <propertyname="username"value="root"/> <propertyname="password"value="123456"/> </dataSource> </environment> </environments>
<!-- 指定MyBatis映射配置文件,这里面除了可以指定映射关系外,还能在里面写SQL操作语句,类似hibernate里的hbm.xml文件 --> <mappers> <mapperresource="cn/andco/mybatis/mapper/UserMapper.xml"/> </mappers> </configuration> |
当然, XML 配置文件中还有很多可以配置的, 在 上面的示例指出的则是最关键的部分。 要注意 XML 头部的声明,需要用来验证 XML 文档正确性。environment 元素体中包含对事 务管理和连接池的环境配置。 mappers 元素是包含所有mapper 映射器) ( 的列表, 这些 mapper 的 XML 文件包含 SQL 代码和映射定义信息。
4.1.3. 创建User实体Bean
package cn.andco.mybatis.model;
import java.io.Serializable;
public class User implements Serializable{
private Integeruserid;
private Stringusername;
private Integeruserage;
public Integer getUserid() { returnuserid; }
public void setUserid(Integer userid) { this.userid =userid; }
public String getUsername() { returnusername; }
public void setUsername(String username) { this.username =username; }
public Integer getUserage() { returnuserage; }
public void setUserage(Integer userage) { this.userage =userage; } } |
4.1.4. 创建UserMapper.xml
<?xmlversion="1.0"encoding="UTF-8"?> <!DOCTYPEmapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mappernamespace="cn.andco.mybatis.model.User"> <!-- addUser --> <insertid="addUser"parameterType="cn.andco.mybatis.model.User"> INSERT INTO user(username,userage)VALUES(#{username},#{userage}) </insert> </mapper> |
4.1.5. 增加数据测试
package test.cn.andco.mybatis;
import java.io.Reader;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test;
import cn.andco.mybatis.model.User;
public class TestMyBatis {
@Test public void addUser() throws Exception{ User user = new User(); user.setUsername("小红"); user.setUserage(20);
//创建SqlSessionFactory String resource = "mybatis.xml"; Reader reader = Resources.getResourceAsReader(resource); SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(reader);
//获得会话SqlSession对象 SqlSession session = sqlSessionFactory.openSession();
//执行增加数据 //Mapper.xml(namespace).id session.insert("cn.andco.mybatis.model.User.addUser",user);
//提交数据 session.commit();
//关闭资源 session.close(); } } |
5. 加入日志
Log4j
log4j.rootLogger=DEBUG,A1 log4j.logger.com.taotao = DEBUG log4j.logger.org.mybatis = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n |
5.1. MyBatis工作流程
首先呢,我们配置mybatis的配置文件,sqlmapConfig.xml这个文件(虽然名称不固定,但是这是约定成俗的写法)。 然后,通过配置文件,加载mybatis的运行环境,创建SqlSessionFactory的会话工厂(按照单例方式创建,多例也可以,不过浪费资源严重)。 接下来,通过会话工厂(SqlSessionFactory)去创建会话(SqlSession)。这个接口是线程不安全的,所以建议应用在方法体内。 剩下的就是,调用sqlSession的方法去操作数据库了。操作完成后,进行下事务的commit()的方法。然后释放资源关闭sqlSession。 |
5.2. MyBatis工具类封装
因为SqlSessionFactory对象创建需要消耗大量资源,所以一个应用,最好只创建一个SqlSessionFactory对象。
提取一个工具类,可以通过工具类获取SqlSessionFactory,也可以通过工具类获取SqlSession对象。
package cn.andco.mybatis; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.Reader; /**** * author:shenkunlin * date:2017/10/811:59 * description:cn.andco.mybatis.mybatis1 * MyBatis工具类 * 创建SQLSessionFactory对象 **/ public class MyBatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //创建SqlSessionFactory String resource = "mybatis.xml"; //加载读取核心配置文件 Reader reader = Resources.getResourceAsReader(resource); //根据核心配置文件创建SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { e.printStackTrace(); } } /*** * 获得SQLSessionFactory对象 * @return */ public static SqlSessionFactory getSqlSessionFactory(){ return sqlSessionFactory; } /** * 获得SqlSession会话 * @return */ public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } } |
5.3. 基于MyBatis查询数据
5.3.1. 查询数据测试类(不传参)
@Before public void init(){ //获得SqlSession会话 session=MyBatisUtils.getSqlSession(); } @Test public void testSelect(){ //查询List集合,不带参数 List<User> users = session.selectList("cn.andco.mybatis.model.User.getAllUser"); for (User user : users){ System.out.println(user); } } |
5.3.2. UserMapper.xml配置
<!--resultType标示查询结果集需要封装的数据类型,也就是我们需要接受的数据对象的类型--> <select id="getAllUser" resultType="cn.andco.mybatis.model.User"> SELECT * FROM user </select> |
测试结果:
User [id=10, name=小红2009,age=21]
5.3.3. 查询传参演示
这里只举一个例子,其他的都一样
@Test public void getUser(){ //3、执行操作----sqlSession.selectOne(key) //其中key=Mapper.xml(namespace).(insert、delete、select、update)id User user = sqlSession.selectOne("cn.andco.mybatis.model.User.getUser",11);
System.out.println(user); //4、关闭资源 if(sqlSession!=null){ sqlSession.close(); } } |
UserMapper.xml
<!-- getUser --> <selectid="getUser"resultType="cn.andco.mybatis.model.User"parameterType="int"> SELECT * FROM user WHERE id=#{id} </select> |
5.3.4. 增加数据
增加数据测试类
/*** * 增加测试 */ @Test public void add() { User user = new User("小猪", 20); sqlSession.insert("cn.andco.mybatis.model.User.add",user); sqlSession.commit(); System.out.println(user); } |
增加数据Mapper.xml方法
这里#{xx} x是指对应参数的属性,这里的参数是User对象的实例
<!-- add --> <insertid="add"parameterType="cn.andco.mybatis.model.User"> INSERT INTO user(name,age) VALUES(#{name},#{age}) </insert> |
问题来了,看打印结果
User [id=null, name=小猪,age=20]
这里主键数据库是自增,但这里并未获取到数据,如何才能得到它?
5.3.5. 获取自增返回主键
5.3.5.1. 通过SelectKey获取主键
|
<!-- add --> <insertid="add"parameterType="cn.andco.mybatis.model.User"> <selectKeykeyColumn="id"keyProperty="id"resultType="int"order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO user(name,age) VALUES(#{name},#{age}) </insert> |
5.3.5.2. 配置insert节点获取自增主键值
<insertid="add"parameterType="cn.andco.mybatis.model.User"useGeneratedKeys="true"keyProperty="id"> INSERT INTO user(name,age) VALUES(#{name},#{age}) </insert> |
5.3.5.3. mod
<!-- add --> <insertid="add"parameterType="cn.andco.mybatis.model.User"> <selectKeykeyColumn="id"keyProperty="id"resultType="string"order="BEFORE"> select uuid() </selectKey> INSERT INTO user(id,name,age) VALUES(UUID(),#{name},#{age}) </insert> |
5.3.6. MyBatis修改数据
测试方法
@Test public void modify(){ User user = new User("小猪1", 20); user.setId("10"); sqlSession.insert("cn.andco.mybatis.model.User.modify",user); sqlSession.commit(); } |
Mapper.xml配置
<!-- modify --> <updateid="modify"parameterType="cn.andco.mybatis.model.User"> UPDATE user SET name=#{name}, age=#{age} WHERE id=#{id} </update> |
5.3.7. 删除数据
测试方法
@Test public void delete(){ sqlSession.insert("cn.andco.mybatis.model.User.delete","10"); sqlSession.commit(); } |
Mapper.xml
<!-- delete --> <deleteid="delete"parameterType="string"> DELETE FROM user WHERE id=#{id} </delete> |
5.4. MyBatis动态代理Dao包装
5.4.1. 基于动态代理实现DaoI介绍
按照正常流程,用户发起请求,请求会从Controller逐步转发到DaoImpl,但咱们刚才整个过程并没用到Dao。 MyBatis官方提供了另一套Dao实现机制,通过给接口DaoI提供一个代理类,来动态产生接口的实现类,每次DaoI调用的方法名字其实就是对应的Mappser.xml里4个节点所对应的id值。
5.4.2. 动态代理实现相关约束
1. namespace必需是接口的全路径名
2. 接口的方法名必需与映射文件的sql id一致
3. 接口的方法输入参数必需与映射文件的parameterType类型一致
4. 接口的方法返回类型必须与映射文件的resultType类型一致
5.5. 基于MyBatis动态代理实现增删改查
5.5.1. 创建UserMapper接口
这个接口其实就是我们以前常写的UserDaoI接口,就缺实现类。
public interface UserMapper {
public User getUser(Integer id);
public int add(User user);
/** * @param user */ public int modify(User user);
/** * @param id */ public int delete(String id);
} |
5.5.2. 修改UserMapper.xml
没错,这里只需要修改命名空间即可,命名空间要指向对应的接口全限定名。
<mappernamespace="cn.andco.mybatis.UserMapper"> |
5.5.3. 测试方法修改
这里每次只需要传入对应的Mapper接口的Class,然后调用它对应的方法传入方法所需参数即可。开发中,我们更推荐采用这种方式。
public class UserTest {
private SqlSessionFactorysqlSessionFactory;
private SqlSessionsqlSession;
@Before public void init() { // 1、获得SqlSessionFactory对象 sqlSessionFactory = SqlSessionFactoryManager.newinstance().createSqlSessionFactory();
// 2、创建SqlSession对象 sqlSession = sqlSessionFactory.openSession(); }
/*** * 删除操作 */ @Test public void delete() { String id = "11"; sqlSession.getMapper(UserMapper.class).delete(id); sqlSession.commit(); }
/*** * 更新数据 */ @Test public void modify() { User user = new User("小猪1", 20); user.setId("11"); sqlSession.getMapper(UserMapper.class).modify(user); sqlSession.commit(); }
/*** * 增加测试 */ @Test public void add() { User user = new User("小猪", 20); sqlSession.getMapper(UserMapper.class).add(user); sqlSession.commit(); System.out.println(user); }
/*** * 查询测试 */ @Test public void getUser() { // 3、执行操作----sqlSession.selectOne(key) // 其中key=Mapper.xml(namespace).(insert、delete、select、update)id //User user = sqlSession.selectOne("cn.andco.mybatis.model.User.getUser", 11); User user = sqlSession.getMapper(UserMapper.class).getUser(12);
System.out.println(user); }
@After public void destroy() { // 4、关闭资源 if (sqlSession !=null) { sqlSession.close(); } } } |
6. 核心配置文件
核心配置文件mybatis.xml文件的属性有多个,如下图:
6.1. Properties属性
通常可以用它来加载外部文件参数注入到对应所需变量里,例如jdbc.properties来注入到数据源中。
<!-- 定义属性,也可以将properties文件属性注入进来 属性优先级是先加载内部属性,再加载外部属性 由于外部属性后被加载,可以将先加载的内部属性替换掉 --> <properties resource="jdbc.properties"> <propertyname="db.driver"value="com.mysql.jdbc.Driver"/> <propertyname="db.url"value="jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8"/> <propertyname="db.username"value="root1"/> <propertyname="db.password"value="123456"/> </properties>
<!-- MyBatis数据源环境配置,这里的default指定的数据源环境必须存在 --> <environmentsdefault="development"> <!-- 数据源环境配置 --> <environmentid="development"> <transactionManagertype="JDBC"/> <dataSourcetype="POOLED"> <propertyname="driver"value="${db.driver}"/> <propertyname="url"value="${db.url}"/> <propertyname="username"value="${db.username}"/> <propertyname="password"value="${db.password}"/> </dataSource> </environment> </environments> |
6.2. TypeAliases别名配置
<!-- 别名设置 --> <typeAliases> <!-- 单个别名 --> <typeAliastype="cn.andco.mybatis.model.User"alias="TUser"/>
<!-- package表示要给哪个包下的所有类取别名 --> <packagename="cn.andco.mybatis.model"/> </typeAliases> |
6.3. Mappers映射器配置
<mappers> <!-- <mapper resource="cn/andco/mybatis/model/UserMapper.xml" /> -->
<!-- 使用class类映射器,有3点要求: 1、class=接口的全限定名 2、当前接口和对应的Mapper.xml必须在同一包下 3、接口名字和Mapper.xml映射文件必须一样 --> <!-- <mapper class="cn.andco.mybatis.UserMapper" /> -->
<!-- 包扫描器有2点要求: 1、当前接口和对应的Mapper.xml必须在同一包下 2、接口名字和Mapper.xml映射文件必须一样 --> <packagename="cn.andco.mybatis"/> </mappers> |
7. maven附录:
<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.andco</groupId> <artifactId>mybatisdemo</artifactId> <version>0.0.1-SNAPSHOT</version>
<!-- 集中定义依赖版本号 --> <properties> <mybatis.version>3.2.8</mybatis.version> <mysql.version>5.1.32</mysql.version> <slf4j.version>1.6.4</slf4j.version> <commons-lang3.version>3.3.2</commons-lang3.version> <commons.logging.version>1.1.1</commons.logging.version> <log4j.version>1.2.17</log4j.version> <org.slf4j.api.version>1.7.5</org.slf4j.api.version> <org.apache.logging.log4j.version>2.0.2</org.apache.logging.log4j.version> <org.slf4j.version>1.7.5</org.slf4j.version> <junit.version>4.10</junit.version> </properties>
<!-- 依赖版本 --> <dependencies> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency>
<!-- MySqL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency>
<!-- 日志 --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>${commons.logging.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${org.slf4j.api.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${org.apache.logging.log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${org.slf4j.version}</version> </dependency>
<!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency>
</dependencies>
<build> <plugins> <!-- 资源文件拷贝插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.5</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- java编译插件 --> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins>
<resources> <!-- 使用Maven部署的时候,xml和properties配置文件也一起部署到Tomcat --> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <!-- 默认是以下配置 --> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>
</build> </project> |