文档章节

Mybatis应用学习——简单使用示例

江左煤郎
 江左煤郎
发布于 11/22 00:16
字数 2858
阅读 13
收藏 3

1. 传统JDBC程序中存在的问题

    1. 一个简单的JDBC程序示例:

public class JDBCDemo {
	public static void main(String[] args) {
		Connection con=null;
		PreparedStatement statement=null;
		ResultSet rs=null;
		try {
//首先获取到与数据库的连接对象
			Properties pro=new Properties();
			pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties"));
			String driver=pro.getProperty("driver");
			String user=pro.getProperty("user");
			String url=pro.getProperty("url");
			String password=pro.getProperty("password");
			Class.forName(driver);
			con=DriverManager.getConnection(url, user, password);
//执行sql语句
			String id="1";
			String sql="select * from employee where id=?";
			statement=con.prepareStatement(sql);
			statement.setInt(1,2);
			rs=statement.executeQuery();
			while(rs.next()){
				System.out.println(rs.getInt("id"));
				System.out.println(rs.getString(2));
				System.out.println(rs.getFloat("salary"));
				System.out.println();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
//关闭资源
			try {
				if(rs!=null){
					rs.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs=null;
			try {
				if(statement!=null){
					statement.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement=null;
			try {
				if(con!=null){
					con.close();		
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			con=null;
		}
	}
}

    2. 上面代码造成的问题:

  • 频繁的创建和释放数据库连接对象,极大消耗数据库性能,解决:可以通过数据库连接池技术(c3p0、DBCP、druid)来管理数据库连接对象
  • SQL语句硬编码到Java代码中,也就是把SQL语句写死了,如果想要执行其他SQL语句则必须修改代码或重写一份,不利于系统维护。解决办法就是将SQL语句提取出来,可以写在xml文件中,如果需要修改,只需要修改SQL语句即可,不需要修改Java源码。
  • PreparedStatement 对象中需要设置SQL语句中占位符处所需要设置的参数,此处存在参数硬编码,所以和SQL语句一样,不利于系统维护。解决办法就是将SQL语句和参数提取出来,写在同一个xml文件中,如果需要修改,只需要修改配合文件即可,不需要修改Java源码。
  • 从ResultSet结果集中获取数据时,存在硬编码,因为指定死了要获取的字段数据。解决办法就是将查询到的结果集中的字段自动映射为Java对象
  • 代码重复问题,在上面的程序代码中,获取数据库连接对象以及关闭释放资源的代码块是不变的,唯一有变化的就在于执行SQL语句的代码块,如果开发多个SQL语句的程序,获取数据库连接对象以及关闭释放资源的代码块将会高度重复,所以我们可以将这两部分代码块提取出来,而将SQL语句和参数作为一个参数进行传入,然后执行

2. Mybatis框架介绍

    1. mybatis是一个持久层的框架,是apache下的顶级项目。mybatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,需要程序员编写sql语句的主体,而sql语句中的字段等参数由mybatis添加)满足需要sql语句。mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象(输出映射)。

    2. 框架执行流程:

    3. 详细介绍及部分原理可以参考:MyBatis的架构设计以及实例分析

3. 框架使用的简单实例

    1. 编写Mybatis的配置文件:

<?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>
	<settings>
		<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
	<!-- 与Spring整合后environments标签的所有内容都要删除 -->
	<environments default="environment">
		<environment id="environment">
			<!-- 配置JDBC事务管理, mybatis进行事物控制mybatis-->
			<transactionManager type="JDBC" />
			<!-- 配置数据库连接池,使用mybatis提供的-->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://localhost:3306/cloud_note?useUnicode=true&amp;characterEncoding=utf8" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>
	<!--指定映射文件位置,与Spring整合后,只需要保存该标签即可-->
	<mappers>
		<mapper resource="mapper/Usermapper.xml"/>
	</mappers>
</configuration> 

    2. 编写User的pojo类,用作输入或输出映射,注意,该pojo类必须符合JavaBean规范,而且类中的属性名要与数据库表中的字段名相同

import java.io.Serializable;
public class User implements Serializable{
	private String cn_user_id;
	private String cn_user_name;
	private String cn_user_password;
	private String cn_user_token;
	private String cn_user_nick;
	public String getCn_user_id() {
		return cn_user_id;
	}
	public void setCn_user_id(String cn_user_id) {
		this.cn_user_id = cn_user_id;
	}
	public String getCn_user_name() {
		return cn_user_name;
	}
	public void setCn_user_name(String cn_user_name) {
		this.cn_user_name = cn_user_name;
	}
	public String getCn_user_password() {
		return cn_user_password;
	}
	public void setCn_user_password(String cn_user_password) {
		this.cn_user_password = cn_user_password;
	}
	public String getCn_user_token() {
		return cn_user_token;
	}
	public void setCn_user_token(String cn_user_token) {
		this.cn_user_token = cn_user_token;
	}
	public String getCn_user_nick() {
		return cn_user_nick;
	}
	public void setCn_user_nick(String cn_user_nick) {
		this.cn_user_nick = cn_user_nick;
	}
	@Override
	public String toString() {
		return "User [cn_user_id=" + cn_user_id + ", cn_user_name=" + cn_user_name + "]";
	}
	
}

    3. 编写映射文件Mapper.xml:映射文件中包含了Java对象和数据库表之间的映射关系,SQL语句所需要的输入参数映射和输出参数映射都在Mapper.xml中定义,该文件命名必须为*Mapper.xml,比如UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!-- 首先要引入mybatis的标签约束 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace属性值表示命名空间,用于SQL的隔离-->
<mapper namespace="dao.UserDao">

	<!--select标签就表示定义select查询SQL语句,id就表示该语句的标示,当引用该语句时,就通过namespace + .id来引用该SQL语句  -->
	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
		select * from cn_user where cn_user_id=#{id}
	</select>

	<!--insert标签就表示定义insert插入数据SQL语句  -->
	<insert id="insertUser" parameterType="entity.User">
		<!-- selectKey标签为insert标签特有的子标签,用来将生成的主键回写到传入的pojo对象中
		 1. keyProperty指定回写的pojo对象中的属性名
		 2. order指定该回写操作是在SQL语句执行前还是执行后
		 3. resultType指定回写的数据映射的Java类型
		 -->
		<selectKey keyProperty="cn_user_id" order="BEFORE" resultType="string">
		<!-- 通过mysql内置的UUID()函数来生成主键并注入至传入User类型对象的cn_user_id属性中
		然后再将此对象作为参数传入sql语句执行 -->
			select uuid()
		</selectKey>
		<!-- 在自增id的数据库中可使用last_insert_id()函数来获取最后一次执行插入数据的主键id
		在执行完SQL语句后,将新插入数据的id返回给对象并且设置为cn_user_id
		注意:自增主键的数据库的主键id其类型必须为int型
		<selectKey keyProperty="cn_user_id" order="AFTER" resultType="int">
			select last_insert_id()
		</selectKey>
		 -->
		insert into cn_user(cn_user_id,cn_user_name,cn_user_password,cn_user_token,cn_user_nick) 
		values(#{cn_user_id},#{cn_user_name},#{cn_user_password},#{cn_user_token},#{cn_user_nick})
		<!--#{}相当于JDBC代码中的SQL预编译语句中的 ? ,表示占位符,如果接受的参数类型数parameterType
		为Java中string、int、long等,则#{}中的字符串可以随便写,但一般建议写为该参数对应的数据库表的字段名;
		但如果该参数为自定义的pojo类,如User类,则#{}中的字符串
		必须为对应的pojo对象中的属性名,Mybatis会通过OGNL获取pojo对象中对应属性的值-->
	</insert>

	<!-- 通过cn_user_name字段进行模糊查询,可能会返回多条数据,但是每条数据所映射的对象仍然是entity.User类,resultType指的是每一条数据所映射的类型 -->
	<select id="selectUserLike" parameterType="string" resultType="entity.User">
		<!-- 该方式是将%写在SQL语句中,那么代码中就不用在传入的字符串两端添加% -->
		select * from cn_user where cn_user_name like '%#{name}%'
		<!-- 该方式是在代码对传入的参数字符串两端添加% -->
		<!-- select * from cn_user where cn_user_name like '#{name}' -->
	</select>
</mapper>

    4. 编写测试程序:

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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.Before;
import org.junit.Test;

import entity.User;
//Mybatis框架底层实现数据库交互操作过程
public class TestCase1 {
	private SqlSessionFactory ssf;
	@Before
	public void init() throws IOException{
		//1.实例SqlSessionFactoryBuilder
		String resource="conf/SqlMapConfig.xml";
		InputStream s=Resources.getResourceAsStream(resource);
		SqlSessionFactoryBuilder ssfb=new SqlSessionFactoryBuilder();
		//2.通过SqlSessionFactoryBuilder对象加载mybatis环境配置文件,获取SqlSessionFactory对象
		ssf=ssfb.build(s);
	}
	@Test
	public void testFindUserbyId(){
		//3.通过SqlSessionFactory对象来获取SqlSession对象,与数据库进行交互
		SqlSession session=ssf.openSession();
		User user=session.selectOne("dao.UserDao.findUserById", "03590914-a934-4da9-ba4d-b41799f917d1");
		System.out.println(user.toString());
		session.close();
	}
	@Test
	public void testInsertUser(){
		User user=new User();
		user.setCn_user_name("hop");
		user.setCn_user_password("123546");
		SqlSession session=ssf.openSession();
		session.insert("dao.UserDao.insertUser", user);
		System.out.println(user.toString());//查看回写的用户id
		session.close();
	}
	@Test
	public void testSelectUserLike(){
		SqlSession session=ssf.openSession();
		List<User> list=session.selectList("dao.UserDao.selectUserLike", "测试");
		//List<User> list=session.selectList("dao.UserDao.selectUserLike", "%测试%");
		session.close();
	}
}

    5. 关于Mapper映射文件中的部分重点讲解:

  • #{ }:表示一个占位符号,#{}接收输入参数,类型可以是简单类型,如integer、long等,如果接收简单类型,#{}中可以写成value或其它名称;

    如果输入参数类型为pojo类,而且该类中还包括一个pojo类型的属性,#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值,比如输入参数类型为User类,User类内还有一个book属性,该属性为Book类,是一个pojo类,Book类中包括String类型的name属性,那么如果要取name属性值,就可以写为#{book.name}。

4. 通过Mybatis开发Dao

4.1 通过原始dao开发

    1. 定义Dao接口:

import entity.User;
public interface UserDao {
	public User findUserById(String id);
	public void insertUser(User user);
}

    2. 通过Mybatis开发Dao接口的实现类:

import java.io.IOException;
import java.io.InputStream;
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.Before;
import entity.User;
public class UserDaoImp implements UserDao {
	private static SqlSessionFactory ssf;
	static{
		String resource="conf/SqlMapConfig.xml";
		InputStream s;
		try {
			s = Resources.getResourceAsStream(resource);
			ssf=new SqlSessionFactoryBuilder().build(s);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	@Override
	public User findUserById(String id) {
		SqlSession session=ssf.openSession();
		User user=session.selectOne("findUserById",id);
		session.close();
		return user;
	}
	@Override
	public void insertUser(User user) {
		SqlSession session=ssf.openSession();
		session.insert("insertUser", user);
		session.close();
	}
}

    3. 原始dao开发存在的问题:

  • dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
  • 调用sqlsession方法时将statement的id硬编码了
  • 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

4.2 Mapper代理开发

    1. 该只需要编写好Mapper接口(Mapper接口也就是Dao接口),定义数据操作方法即可,编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。

    2. 开发规范有:

  • 在mapper.xml中,namespace属性值应为mapper接口的全限定名
    package dao;
    import entity.User;
    public interface UserDao {
    	public User findUserById(String id);
    	public void insertUser(User user);
    }
    <?xml version="1.0" encoding="UTF-8" ?>  
    <!-- 首先要引入mybatis的标签约束 -->
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="dao.UserDao">
    
    </mapper>

     

  • mapper.java接口中的方法名和mapper.xml中statement的id一致
    <?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">
    
    <mapper namespace="dao.UserDao">
    	
    	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
    		select * from cn_user where cn_user_id=#{id}
    	</select>
    	
    	<insert id="insertUser" parameterType="entity.User">
    		<selectKey keyProperty="cn_user_id" order="BEFORE" resultType="string">
    			select uuid()
    		</selectKey>
    		insert into cn_user(cn_user_id,cn_user_name,cn_user_password,cn_user_token,cn_user_nick) 
    		values(#{cn_user_id},#{cn_user_name},#{cn_user_password},#{cn_user_token},#{cn_user_nick})
    	</insert>
    </mapper>

     

  • mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
  • mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。

    3. 满足以上开发规范后,可通过测试代码验证:

/**
 * 测试Mybatis框架通过Mapper代理方法实现Dao接口
 */
public class TestCase3 {
	private SqlSessionFactory ssf;
	@Before
	public void init() throws IOException{
		String resource="conf/SqlMapConfig.xml";
		InputStream s=Resources.getResourceAsStream(resource);
		SqlSessionFactoryBuilder ssfb=new SqlSessionFactoryBuilder();
		ssf=ssfb.build(s);
	}
	@Test
	public void testFindUserbyId(){
		SqlSession session=ssf.openSession();
		UserDao dao=session.getMapper(UserDao.class);
		User user=dao.findUserById("03590914-a934-4da9-ba4d-b41799f917d1");
		System.out.println(user.toString());
	}
}

    4. Mapper接口方法的参数只能有一个:这个问题其实很简单,就是通过包装类将所需要的多个参数包装成一个类

© 著作权归作者所有

共有 人打赏支持
江左煤郎
粉丝 26
博文 81
码字总数 204804
作品 0
西安
后端工程师
私信 提问
Spring Boot 学习资料收集

导读: 从第一次接触Spring Boot 至今已经有半年多了,在这期间也浏览了许多和Spring Boot 相关的书籍及文章,公司里面的许多项目也一直在使用Spring Boot。 关于Spring Boot的一些看法: Sp...

yangrd
2017/03/02
0
0
MyBatis中如何通过继承SqlSessionDaoSupport来编写DAO(二)

(本文示例工程源代码下载地址:http://down.51cto.com/data/1975295) 在上一篇博文的最后,介绍了使用@PostConstruct注解标注StudentDao的init方法,这样在Spring完成依赖注入后此方法即会...

NashMaster2011
2015/01/14
0
0
如何在MyBatis-3.2.7中使用Log4j2 rc2——MyBatis学习笔记之十九

前天我上传了我的MyBatis系列课程(http://edu.51cto.com/course/course_id-1110.html)的第六讲,主要内容是如何使用Log4j2(具体版本为v2.0-rc1)为MyBatis 3.2.7配置日志。实际上目前最新...

NashMaster2011
2014/07/09
0
0
史上最简单的 MyBatis 教程(一)

1 简介 MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架,其几乎消除了所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始...

qq_35246620
2017/02/02
0
0
MyBatis多对多保存示例——MyBatis学习笔记之十七

前几天有网友问到MyBatis多对多的问题,不过愧对网友厚爱的是,最近一直忙,直到现在才有时间处理此事。今天就先写一个多对多保存的示例,算是对这位网友的初步回应,以后会有更多相关的博文...

NashMaster2011
2013/08/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

easyui tree

<tr> <th><spring:message code="wf.borrow.examiner"/></th> <td> <input id="inp-examiner1" type="text" name="examiner1" style="width:197px;height:20px;" data-options="required:tru......

小兵胖胖
10分钟前
0
0
内存性能的正确解读

一台服务器,不管是物理机还是虚拟机,必不可少的就是内存,内存的性能又是如何来衡量呢。 1. 内存与缓存 现在比较新的CPU一般都有三级缓存,L1 Cache(32KB-256KB),L2 Cache(128KB-2MB)...

阿里云云栖社区
13分钟前
0
0
微服务架构:Zuul 1.0 和 2.0 我们该如何选择?

在今年5月中,Netflix终于开源了它的支持异步调用模式的Zuul网关2.0版本,真可谓千呼万唤始出来。从Netflix的官方博文[附录1]中,我们获得的信息也比较令人振奋: The Cloud Gateway team a...

大木老师故事的小黄花
13分钟前
0
0
基础掌握

哪些是基础功呢?我觉得包括: 数据结构和算法:链表、队列、栈、堆、树(RBT, B/B+)、跳表、哈希、图;查找(二分、bst)、排序(冒泡、插入、快排、归并、堆排、希尔)、递归、归并、回溯、...

边鹏_尛爺鑫
14分钟前
0
0
Android APP的安装路径

一. Android应用安装路径有两种情况: system/app 系统自带的应用程序,无法删除。root后可以删除,注意可能造成系统崩溃,不过有的垃圾捆绑软件只能这么删除了 data/app 用户程序安装的目录,...

天王盖地虎626
17分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部