Mybatis快速上手使用示例

Mybatis快速上手使用示例

本mybatis使用示例是通过Eclipse创建的maven项目,所有相关依赖都是通过配置pom.xml加载的,这里不做详述了。在详细讲解使用示例之前,先大概说下使用mybatis需要哪几步及整体的层次结构,结合示例代码的层次结构来说明下,参考下图:

1、mapper包中包含定义了访问数据库方法的接口类文件和配置了接口方法要执行的sql语句的xml文件,对应的类文件和xml的名称要保持一致。

2、pojos包中定义的类可以简单的理解为映射到数据库表字段的类,通过这样一个映射关系,对pojo对象的操作就会传递到相应的数据表字段上。作为示例,这里创建了Product和User类,相应的数据库中得有product和user表,表结构如下:

ProductUser
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`productname` varchar(32) NOT NULL,
`price` float DEFAULT ‘0’,
`type` int(2) DEFAULT ‘0’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT ‘用户名称’,
`phone` int(11) DEFAULT NULL COMMENT ‘电话’,
`sex` char(1) DEFAULT NULL COMMENT ‘性别’,
`address` varchar(256) DEFAULT NULL COMMENT ‘地址’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8

3、tests包中定义了几个使用mybatis的测试类

4、resources目录下是三个配置文件:包含了数据库连接信息、log4j日志配置以及最关键的mybatis配置,mybatis-config.xml中比较关键的是mapper部分,会将mapper包中的类与sql配置建立起映射关系,在运行时会根据该映射关系生成实现了接口方法的虚拟类。该部分为基础公共配置,这里直接把这三个文件内容列出来,后边的详解中不会再单独列出。

db.propertieslog4j.propertiesmybatis-config.xml
jdbc.driver=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://192.168.1.5:3306/test

jdbc.username=test

jdbc.password=123qwe

log4j.rootLogger=DEBUG,stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] – %m%n

<?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>
<!– 加载db配置文件 –>
<properties resource=”db.properties”>
<!–properties中还可以配置一些属性名和属性值 ,可以不通过引用resource文件,直接定义数据库配置信息 –>
<!– <property name=”jdbc.driver” value=””/> –>
</properties>
<!– 全局配置参数,需要时再设置,这里配置了数据库超时时间和使用的日志库,如果不指定mybatis会自动查找可用的日志库 –>
<settings>
<setting name=”defaultStatementTimeout” value=”5″/>
<setting name=”logImpl” value=”LOG4J”/>
</settings>
<!– 别名定义 –>
<typeAliases>
<!– 针对单个类定义别名,别名只针对xml中的引用生效–>
<typeAlias type=”com.vdcoding.mybatis.mapper.UserMapper” alias=”usermapper”/>
<!– 批量别名定义 指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以) –>
<package name=”com.vdcoding.mybatis.pojos”/>
</typeAliases>
<environments default=”development”>
<environment id=”development”>
<!– 使用jdbc事务管理,事务控制由mybatis–>
<transactionManager type=”JDBC” />
<!– 数据库连接池,由mybatis管理–>
<dataSource type=”POOLED”>
<property name=”driver” value=”${jdbc.driver}”/>
<property name=”url” value=”${jdbc.url}”/>
<property name=”username” value=”${jdbc.username}”/>
<property name=”password” value=”${jdbc.password}”/>
</dataSource>
</environment>
<environment id=”production”>
<transactionManager type=”JDBC” />
<dataSource type=”POOLED”>
<property name=”driver” value=”${jdbc.driver}”/>
<property name=”url” value=”${jdbc.url}”/>
<property name=”username” value=”${jdbc.username}”/>
<property name=”password” value=”${jdbc.password}”/>
</dataSource>
</environment>
</environments>
<!– 加载 映射文件 –>
<mappers>
<!–通过resource方法一次加载一个映射文件 –>
<!–<mapper resource=”UserMapper.xml”/>–>
<!– 通过mapper接口加载单个 映射文件 遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录 中
上边规范的前提是:使用的是mapper代理方法 –>
<!– <mapper class=”cn.vdcoding.mybatis.mapper.UserMapper”/> –>
<!– 批量加载mapper,指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载,需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中–>
<package name=”com.vdcoding.mybatis.mapper”/>
</mappers>
</configuration>

以上是整个示例代码的层级结构及各部分的作用,接下来详解介绍下实际的使用,分为两种使用方法:

一、通过配置sql mapper的xml文件,大部分情况都是这样使用

1、定义User.java类,该类的各属性映射到user表的各个字段

package com.vdcoding.mybatis.pojos;


public class User {
    //属性名和数据库表的字段对应  
    private int id;  
    private String username;
    private String sex;
    private int phone;
    private String address;
    public int getId() {  
        return id;  
    }  

    public String getUsername() {  
        return username;  
    }  
    public void setUsername(String username) {  
        this.username = username;  
    }  
    public String getSex() {  
        return sex;  
    }  
    public void setSex(String sex) {  
        this.sex = sex;  
    }  
    public int getPhone() {  
        return phone;  
    }  
    public void setPhone(int phone) {  
        this.phone = phone;  
    }  
    public String getAddress() {  
        return address;  
    }  
    public void setAddress(String address) {  
        this.address = address;  
    }  
    @Override  
    public String toString() {  
        return "User [id=" + id + ", username=" + username + ", sex=" + sex + "]";  
    }  
}

2、创建接口类UserMapper.java,其中定义了操作数据的CRUD方法;创建UserMapper.xml,定义对应接口方法要执行的sql语句

package com.vdcoding.mybatis.mapper;

import java.util.List;
import java.util.Map;

import com.vdcoding.mybatis.pojos.*;  
  
public interface UserMapper { 
  
    //根据用户名列模糊查询用户列表  
    public List<User> findUserByName(String name)throws Exception;
    //根据id更新用户信息
    public int updateUser(User user);
    
    public <T> int addUser(Map<T, T> map);
      
}
<?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="com.vdcoding.mybatis.mapper.UserMapper">
    <select id="findUserByName" parameterType="string" resultType="User">  
        SELECT * FROM user 
        <where>
        	<if test="value != null">
        		username LIKE CONCAT('%',#{name},'%');
        	</if>
        </where>
    </select>
    <update id="updateUser" parameterType="User">
    	UPDATE user SET username=#{username},sex=#{sex},address=#{address} WHERE id=#{id}
    </update>
    <insert id="addUser" parameterType="map">
    	INSERT INTO user (username, sex, address) VALUES(#{username},#{sex},#{address})
    </insert>
</mapper>  

关于该xml有几点要特别强调的:

  • 所有sql中用到的占位符是占位符1:#{},也可以使用占位符2:${},使用这种占位符会将对应的值直接拼接到sql中,显而易见的会有sql注入的风险。推荐使用占位符1,会经过特殊的转义处理,更加安全
  • 使用#{}占位符时一定不能用引号包裹,否则会无法识别,导致给占位符传参异常,会报如下错误: Cause: java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1),意思就是在解析sql的时候只发现了1个占位符,但是却传入了2个参数,导致越界。如果必须要用引号括起来,比如sql中的like,则可以使用sql中的CONCAT函数,如下select * from user where username like CONCAT(‘%’,#{name}, ‘%’)。相对的使用${}占位符时,则需要像写sql那样,该加引号的必须得加,否则会报sql语法错误,因为使用${}只是单纯的字符串拼接
  • 对于parameterType为基本类型的sql,通常情况下占位符任意命名都没有问题,比如select * from user where username=#{name},但是如果是动态sql的话,比如例子中id=findUserByName部分的sql,在if条件中test=”value!=null”,则不能改为这样test=”name!=null”,否则会报错:There is no getter for property named ‘name’ in ‘class java.lang.String’。由于mybatis是基于OGNL表达式来查找属性,所以定义mapper xml时,会根据占位符中的名称去对应的parameterType中获取,但是对于基本类型来讲肯定获取不到自定义的属性,此时可以改为例子中那样使用value或者_parameter,至于为什么,直接看下mybatis源码里TextSqlNode.java中定义的一段方法就明白了:
public String handleToken(String content) {
      Object parameter = context.getBindings().get("_parameter");
      if (parameter == null) {
        context.getBindings().put("value", null);
      } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
        context.getBindings().put("value", parameter);
      }
      Object value = OgnlCache.getValue(content, context.getBindings());
      return (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
}

3、搞定上边两步就基本ok了,下面看下如何调用,先定义了个Base.java类,用于创建sqlSession,方便多个测试类重用

package com.vdcoding.mybatis.tests;

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;


public class Base {
	// mybatis配置文件  
	private String resource = "mybatis-config.xml";
	private InputStream is;
	private SqlSessionFactory sessionFactory;
	public SqlSession session;
	
	public Base() throws IOException {
		// 得到配置文件流  
		is = Resources.getResourceAsStream(resource);
		// 通过配置流构建会话工厂
		sessionFactory = new SqlSessionFactoryBuilder().build(is);
		// 通过工厂得到SqlSession ,传入true启用autoCommit,如果无参调用openSession则默认开启一个事物,即需要手动commit
		session = sessionFactory.openSession(true);
	}
	public Base(String env) throws IOException{
		is = Resources.getResourceAsStream(resource);
		//构建sessionFactory时如果传入env参数,则会引用在mybatis-config.xml中id为对应值的environment配置
		//比如传入production,则会引用<environment id="production">中定义的配置来构建数据源,这样便于多数据库访问
		//如果只传入配置文件流is,则会使用mybatis-config.xml中定义的默认配置<environments default="development">  
		sessionFactory = new SqlSessionFactoryBuilder().build(is, env);
		session = sessionFactory.openSession(true);
	}
}
package com.vdcoding.mybatis.tests;

import java.io.IOException;  
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;  
  
import com.vdcoding.mybatis.pojos.User;  

public class MybatisTest {
    public static void main(String...args) throws IOException {  
    	//指定UserMapper.xml文件的全限定名
    	String qualName = "com.vdcoding.mybatis.mapper.UserMapper";
        SqlSession sqlSession = new Base("production").session;  
	    try{
	        // list中的user和映射文件中resultType所指定的类型一致  
	        List<User> list = sqlSession.selectList(qualName + ".findUserByName");  
	        User user = sqlSession.selectOne(qualName + ".findUserByName", "test");
	        user.setSex("9");
	        user.setAddress("China");
	        int num = sqlSession.update(qualName + ".updateUser", user);
	        sqlSession.commit();
	        Map<String,String> map = new HashMap<>();
	        map.put("username", "superman");
	        map.put("sex", "6");
	        map.put("address", "USA");
	        int i = sqlSession.insert(qualName + ".addUser", map);
	        sqlSession.commit();
	        if(i>0) System.out.println("Add user success!");
	        if(num>0) System.out.println("Update user success!");
	        //首次执行时报错无法找到com.mysql.jdbc.Driver, 开始使用的mysql-connector是6.0.6版本,改为5.1.6正常
	        System.out.println(list);
	        System.out.println(user);
    	} finally{
    		sqlSession.close();
    	}
    }  
}

上边展示了通过引入mapper xml文件的全限定名来使用,还可以通过引用类名直接使用,如下所示:

package com.vdcoding.mybatis.tests;

import com.vdcoding.mybatis.mapper.UserMapper;
import com.vdcoding.mybatis.pojos.User;
import java.util.List;
import org.apache.ibatis.session.SqlSession;

public class MybatisTest02 {
	public static void main(String[] args) throws Exception{
		SqlSession session = new Base().session;
		User user = session.selectOne("com.vdcoding.mybatis.mapper.UserMapper.findUserByName", "test");
		System.out.println(user);
		//UserMapper.xml中的namespace一定要写正确,否则会报Invalid bound statement (not found)
		UserMapper mapper = session.getMapper(UserMapper.class);
		List<User> users = mapper.findUserByName("superman");
		System.out.println(users);
		int num = mapper.updateUser(user);
		System.out.println(num);
	}
}

二、不定义mapper xml,直接在mapper接口类中通过mybatis提供的注解为方法绑定要执行的sql,这种方式比较适合sql简单的应用

1、定义Product.java类,该类的各属性映射到product表的各个字段

package com.vdcoding.mybatis.pojos;

public class Product {
	private int id;
	private String productName;
	private Float price;
	private int type;
	
	public Product() {};
	
	public int getId(){
		return id;
	}
	public String getProductName(){
		return productName;
	}
	public void setProductName(String name){
		this.productName = name;
	}
	public Float getPrice(){
		return price;
	}
	public void setPrice(Float price){
		this.price = price;
	}
	public int getType(){
		return type;
	}
	public void setType(int type){
		this.type = type;
	}
	
	@Override
	public String toString(){
		return "Product-" + this.id;
	}

}

2、定义mapper类ProductMapper

package com.vdcoding.mybatis.mapper;

import java.util.List;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.vdcoding.mybatis.pojos.Product;

public interface ProductMapper {
	@Select("select * from product")
	public List<Product> getAllProduct();
	
	@Select("select * from product where id=#{id}")
	public Product getProductById(int id);
	
	@Update("update product set productname=#{productname},price=#{price} where id=#{id}")
	int updateProduct(Product product);
}

3、创建测试类

package com.vdcoding.mybatis.tests;

import com.vdcoding.mybatis.mapper.ProductMapper;
import com.vdcoding.mybatis.pojos.Product;
import java.util.List;
import org.apache.ibatis.session.SqlSession;

public class MybatisTest03{

	public static void main(String[] args) throws Exception{
		SqlSession session = new Base().session;
		ProductMapper mapper = session.getMapper(ProductMapper.class);
		List<Product> products = mapper.getAllProduct();
		System.out.println(products);
	}
}

以上是比较粗略的讲解了下mybatis的用法,旨在帮助使用者快速上手使用,关于mybatis配置文件以及api的详细介绍还请参考mybatis官方文档,文中列出的所有源码可以从我的Github上下载。

说点什么

avatar
  订阅  
提醒