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

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

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

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. 上面代码形成的问题:mysql

  • 频繁的建立和释放数据库链接对象,极大消耗数据库性能,解决:能够经过数据库链接池技术(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. 框架执行流程:sql

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

3. 框架使用的简单实例

    1. 编写Mybatis的配置文件:apache

<?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规范,并且类中的属性名要与数据库表中的字段名相同session

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.xmlmybatis

<?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映射文件中的部分重点讲解:app

  • #{ }:表示一个占位符号,#{}接收输入参数,类型能够是简单类型,如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接口方法的参数只能有一个:这个问题其实很简单,就是经过包装类将所须要的多个参数包装成一个类

相关文章
相关标签/搜索