Mybatis框架 框架概述 框架是什么
它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题,使用框架的好处:
框架封装了很多的细节,使开发者可以使用极简的方式实现功能,大大提高开发效率
三层架构
表现层:用于展示数据
业务层:处理业务需求
持久层:是和数据库交互
持久层的技术解决方案
JDBC技术:
Connection
PreparedStatement
ResultSet
Spring的JdbcTemplate:
Spring中对jdbc的简单封装
Apache的DBUtils:
它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
注意:
以上这些都不是框架
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都是工具类
Mybatis框架概述
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
采用ORM思想
解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。 为了我们能够更好掌握框架运行的内部过程,并且有更好的体验,下面我们将从自定义Mybatis框架开始来学习框架。此时我们将会体验框架从无到有的过程体验,也能够很好的综合前面阶段所学的基础。
ORM: Object Relational Mapping
对象关系映射
简单来说:
就是把数据库表和实体类及实体类的属性对应起来, 让我们可以操作实体类就实现操作数据库表
user User
id userId
user_name username
需要将实体类的属性和数据库表的字段名称保持一致
user user
id id
user_name user_name
Mybatis入门及环境搭建 前期准备 创建用户表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 CREATE TABLE `myuser` ( `id` int (11 ) NOT NULL auto_increment, `username` varchar (32 ) NOT NULL COMMENT '用户名称' , `birthday` datetime default NULL COMMENT '生日' , `sex` char (1 ) default NULL COMMENT '性别' , `address` varchar (256 ) default NULL COMMENT '地址' , PRIMARY KEY (`id` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8; insert into `myuser` (`id` ,`username` ,`birthday` ,`sex` ,`address` ) values (41 ,'老王' ,'2018-02-27 17:47:08' ,'男' ,'北京' ), (42 ,'小二王' ,'2018-03-02 15:09:37' ,'女' ,'北京金燕龙' ), (43 ,'小二王' ,'2018-03-04 11:34:34' ,'女' ,'北京金燕龙' ), (45 ,'传智播客' ,'2018-03-04 12:04:06' ,'男' ,'北京金燕龙' ), (46 ,'老王' ,'2018-03-07 17:37:26' ,'男' ,'北京' ), (48 ,'小马宝莉' ,'2018-03-08 11:44:00' ,'女' ,'北京修正' );
基于xml的Mybatis使用案例 1.创建maven工程 2.添加Mybatis3.4.5的坐标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="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 > com.uestc</groupId > <artifactId > day01_mybaits01</artifactId > <version > 1.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.5</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > <scope > runtime</scope > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.10</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.12</version > </dependency > </dependencies > </project >
3.编写User实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer id; private String username; private Date brithday; private String sex; private String address; public User () {}; public User (Integer id, String username, Date brithday, String sex, String address) { this .id = id; this .username = username; this .brithday = brithday; this .sex = sex; this .address = address; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public Date getBrithday () { return brithday; } public void setBrithday (Date brithday) { this .brithday = brithday; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", brithday=" + brithday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}' ; } }
4.编写持久层接口IUserDao 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { public List<User> findAll () ; }
5. 编写持久层接口的映射文件IUserDao.xml
1 2 3 4 5 6 7 8 9 10 11 12 <?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.uestc.dao.IUserDao" > <select id ="findAll" resultType ="com.uestc.domain.User" > select * from user </select > </mapper >
6. 编写SqlMapConfig.xml配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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 > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="287216" > </property > </dataSource > </environment > </environments > <mappers > <mapper resource ="com/uestc/dao/IUserDao.xml" > </mapper > </mappers > </configuration >
7.编写测试类
读取配置文件
创建SqlSessionFactory
工厂
使用工厂生产Sqlsession对象
使用Sqlssion创建Dao接口的代理对象IUserDao
使用代理对象执行方法
释放资源
注意事项:不要忘记在映射配置中告知mybatis要封装到哪个实体类中
配置的方式:指定实体类的全限定类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.InputStream;import java.util.List;public class MybatisTest { public static void main (String[] args) throws Exception { InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(is); SqlSession session=factory.openSession(); IUserDao userDao=session.getMapper(IUserDao.class ) ; List<User> users=userDao.findAll(); for (User user:users){ System.out.println(user); } session.close(); is.close(); } }
总结 步骤:
创建maven工程并导入坐标
创建实体类和dao的接口
创建Mybatis的主配置文件:SqlMapConfig.xml
创建映射配置文件:IUserDao.xml
注意事项
创建IUserDao.xml
和 IUserDao.java
时名称是为了和我们之前的知识保持一致,在Mybatis中它把持久层的操作接口名称和映射文件也叫作Mapper
, 所以IUserDao
和IUserMapper
是一样的
在IDEA中创建目录时,它和包是不一样的,包在创建时:com.uestc.dao是三级目录,而目录在创建时:com.uestc.dao是一级目录
mybaits的映射配置文件位置必须和dao接口的包结构相同
映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
当我们遵从了第3,4,5点之后,我们在开发中就无序再写dao的实现类
基于注解的mybatis使用案例 把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句,同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名
注意:我们实际在开发中,都是越简便越好,所以都是采用不写dao实现类
的方式。
不管使用XML还是注解配置,但Mybatis是支持写dao实现类的。
1. 在持久层接口中添加注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.uestc.dao;import com.uestc.domain.User;import org.apache.ibatis.annotations.Select;import java.util.List;public interface IUserDao { @Select ("select * from user" ) public List<User> findAll () ; }
2. 修改 SqlMapConfig.xml 使用class属性指定dao接口的全限定类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?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 > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="287216" > </property > </dataSource > </environment > </environments > <mappers > <mapper class ="com.uestc.dao.IUserDao" > </mapper > </mappers > </configuration >
自定义Mybatis框架
本章我们将使用前面所学的基础知识来构建一个属于自己的持久层框架,将会涉及到的一些知识点:工厂模式(Factory工厂模式)
、构造者模式(Builder模式)
、代理模式
,反射
,自定义注解
,注解的反射
,xml解析
,数据库元数据
,元数据的反射
等。
入门案例分析 mybatis在使用代理dao的方式实现增删改查时做什么事呢?
两件事情:
创建代理对象
在代理对象中调用selectList
自定义mybatis能通过入门案例能看到的类
分析流程 selectList方法分析
getMapper方法分析
项目结构
前期准备 1. 创建Maven工程
2. 导入坐标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="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 > com.uestc</groupId > <artifactId > day01_mybaits01</artifactId > <version > 1.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > <scope > runtime</scope > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.10</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.12</version > </dependency > <dependency > <groupId > dom4j</groupId > <artifactId > dom4j</artifactId > <version > 1.1</version > </dependency > <dependency > <groupId > jaxen</groupId > <artifactId > jaxen</artifactId > <version > 1.1.6</version > </dependency > </dependencies > </project >
3. 引入工具类到项目中 工具类:XMLConfigBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 package com.uestc.mybatis.utils;import com.uestc.mybatis.annotations.Select;import com.uestc.mybatis.cfg.Configuration;import com.uestc.mybatis.cfg.Mapper;import com.uestc.mybatis.io.Resources;import org.dom4j.Attribute;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.HashMap;import java.util.List;import java.util.Map;public class XMLConfigBuilder { public static Configuration loadConfiguration (InputStream config) { try { Configuration cfg = new Configuration(); SAXReader reader = new SAXReader(); Document document = reader.read(config); Element root = document.getRootElement(); List<Element> propertyElements = root.selectNodes("//property" ); for (Element propertyElement : propertyElements){ String name = propertyElement.attributeValue("name" ); if ("driver" .equals(name)){ String driver = propertyElement.attributeValue("value" ); cfg.setDriver(driver); } if ("url" .equals(name)){ String url = propertyElement.attributeValue("value" ); cfg.setUrl(url); } if ("username" .equals(name)){ String username = propertyElement.attributeValue("value" ); cfg.setUsername(username); } if ("password" .equals(name)){ String password = propertyElement.attributeValue("value" ); cfg.setPassword(password); } } List<Element> mapperElements = root.selectNodes("//mappers/mapper" ); for (Element mapperElement : mapperElements){ Attribute attribute = mapperElement.attribute("resource" ); if (attribute != null ){ System.out.println("使用的是XML" ); String mapperPath = attribute.getValue(); Map<String,Mapper> mappers = loadMapperConfiguration(mapperPath); cfg.setMappers(mappers); }else { System.out.println("使用的是注解" ); String daoClassPath = mapperElement.attributeValue("class" ); Map<String,Mapper> mappers = loadMapperAnnotation(daoClassPath); cfg.setMappers(mappers); } } return cfg; }catch (Exception e){ throw new RuntimeException(e); }finally { try { config.close(); }catch (Exception e){ e.printStackTrace(); } } } private static Map<String,Mapper> loadMapperConfiguration (String mapperPath) throws IOException { InputStream in = null ; try { Map<String,Mapper> mappers = new HashMap<String,Mapper>(); in = Resources.getResourceAsStream(mapperPath); SAXReader reader = new SAXReader(); Document document = reader.read(in); Element root = document.getRootElement(); String namespace = root.attributeValue("namespace" ); List<Element> selectElements = root.selectNodes("//select" ); for (Element selectElement : selectElements){ String id = selectElement.attributeValue("id" ); String resultType = selectElement.attributeValue("resultType" ); String queryString = selectElement.getText(); String key = namespace+"." +id; Mapper mapper = new Mapper(); mapper.setQueryString(queryString); mapper.setResultType(resultType); mappers.put(key,mapper); } return mappers; }catch (Exception e){ throw new RuntimeException(e); }finally { in.close(); } } private static Map<String,Mapper> loadMapperAnnotation (String daoClassPath) throws Exception { Map<String,Mapper> mappers = new HashMap<String, Mapper>(); Class daoClass = Class.forName(daoClassPath); Method[] methods = daoClass.getMethods(); for (Method method : methods){ boolean isAnnotated = method.isAnnotationPresent(Select.class ) ; if (isAnnotated){ Mapper mapper = new Mapper(); Select selectAnno = method.getAnnotation(Select.class ) ; String queryString = selectAnno.value(); mapper.setQueryString(queryString); Type type = method.getGenericReturnType(); if (type instanceof ParameterizedType){ ParameterizedType ptype = (ParameterizedType)type; Type[] types = ptype.getActualTypeArguments(); Class domainClass = (Class)types[0 ]; String resultType = domainClass.getName(); mapper.setResultType(resultType); } String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String key = className+"." +methodName; mappers.put(key,mapper); } } return mappers; } }
工具类:Executor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package com.uestc.mybatis.utils;import com.uestc.mybatis.cfg.Mapper;import java.beans.PropertyDescriptor;import java.lang.reflect.Method;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.util.ArrayList;import java.util.List;public class Executor { public <E> List<E> selectList (Mapper mapper, Connection conn) { PreparedStatement pstm = null ; ResultSet rs = null ; try { String queryString = mapper.getQueryString(); String resultType = mapper.getResultType(); Class domainClass = Class.forName(resultType); pstm = conn.prepareStatement(queryString); rs = pstm.executeQuery(); List<E> list = new ArrayList<E>(); while (rs.next()) { E obj = (E)domainClass.newInstance(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 1 ; i <= columnCount; i++) { String columnName = rsmd.getColumnName(i); Object columnValue = rs.getObject(columnName); PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass); Method writeMethod = pd.getWriteMethod(); writeMethod.invoke(obj,columnValue); } list.add(obj); } return list; } catch (Exception e) { throw new RuntimeException(e); } finally { release(pstm,rs); } } private void release (PreparedStatement pstm,ResultSet rs) { if (rs != null ){ try { rs.close(); }catch (Exception e){ e.printStackTrace(); } } if (pstm != null ){ try { pstm.close(); }catch (Exception e){ e.printStackTrace(); } } } }
工具类:DataSourceUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.uestc.mybatis.utils;import com.uestc.mybatis.cfg.Configuration;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;public class DataSourceUtil { public static Connection getConnection (Configuration cfg) { try { Class.forName(cfg.getDriver()); return DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword()); } catch (Exception e){ throw new RuntimeException(e); } } }
4. 编写SqlMapConfig.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?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 > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="287216" > </property > </dataSource > </environment > </environments > <mappers > <mapper resource ="com/uestc/dao/IUserDao.xml" > </mapper > </mappers > </configuration >
5. 编写读取配置文件类 Resources 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.uestc.mybatis.io;import java.io.InputStream;public class Resources { public static InputStream getResourceAsStream (String xmlPath) { return Resources.class .getClassLoader ().getResourceAsStream (xmlPath ) ; } }
6. 编写Mapper类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.uestc.mybatis.cfg;public class Mapper { private String queryString; private String resultType; public String getQueryString () { return queryString; } public void setQueryString (String queryString) { this .queryString = queryString; } public String getResultType () { return resultType; } public void setResultType (String resultType) { this .resultType = resultType; } }
7. 编写Configuration配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.uestc.mybatis.cfg;import java.util.HashMap;import java.util.Map;public class Configuration { private String driver; private String url; private String username; private String password; private Map<String, Mapper> mappers=new HashMap<String, Mapper>(); public Map<String, Mapper> getMappers () { return mappers; } public void setMappers (Map<String, Mapper> mappers) { this .mappers.putAll(mappers); } public String getDriver () { return driver; } public void setDriver (String driver) { this .driver = driver; } public String getUrl () { return url; } public void setUrl (String url) { this .url = url; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } }
8. 编写User实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; public User () {}; public User (Integer id, String username, Date brithday, String sex, String address) { this .id = id; this .username = username; this .birthday = brithday; this .sex = sex; this .address = address; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public Date getBirthday () { return birthday; } public void setBirthday (Date brithday) { this .birthday = brithday; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", brithday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}' ; } }
基于XML的自定义mybatis框架 1. 编写持久层接口IUserDao和IUserDao.xml 1 2 3 4 5 6 7 8 9 10 11 12 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { public List<User> findAll () ; }
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8"?> <mapper namespace ="com.uestc.dao.IUserDao" > <select id ="findAll" resultType ="com.uestc.domain.User" > select * from user </select > </mapper >
2. 编写构建者类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.uestc.mybatis.sqlSession;import com.uestc.mybatis.cfg.Configuration;import com.uestc.mybatis.sqlSession.defaults.DefaultSqlSessionFactory;import com.uestc.mybatis.utils.XMLConfigBuilder;import java.io.InputStream;public class SqlSessionFactoryBuilder { public SqlSessionFactory build (InputStream config) { Configuration cfg= XMLConfigBuilder.loadConfiguration(config); SqlSessionFactory factory=new DefaultSqlSessionFactory(cfg); return factory ; } }
3. 编写SqlSessionFactory接口和实现类 1 2 3 4 5 6 7 8 package com.uestc.mybatis.sqlSession;public interface SqlSessionFactory { SqlSession openSession () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.uestc.mybatis.sqlSession.defaults;import com.uestc.mybatis.cfg.Configuration;import com.uestc.mybatis.sqlSession.SqlSession;import com.uestc.mybatis.sqlSession.SqlSessionFactory;public class DefualtSqlSessionFactory implements SqlSessionFactory { private Configuration cfg; public DefualtSqlSessionFactory (Configuration cfg) { this .cfg=cfg; } public SqlSession openSession () { return new DefaultSqlSession(cfg); } }
4. 编写SqlSession接口和实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.uestc.mybatis.sqlSession;public interface SqlSession { <T> T getMapper (Class<T> daoInterfaceClass) ; void close () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.uestc.mybatis.sqlSession.defaults;import com.uestc.mybatis.cfg.Configuration;import com.uestc.mybatis.cfg.Mapper;import com.uestc.mybatis.sqlSession.SqlSession;import com.uestc.mybatis.sqlSession.proxy.MapperProxy;import com.uestc.mybatis.utils.DataSourceUtil;import javax.sql.DataSource;import java.lang.reflect.Proxy;import java.sql.Connection;import java.sql.SQLException;public class DefaultSqlSession implements SqlSession { private Configuration cfg; private Connection conn; public DefaultSqlSession (Configuration cfg) { this .cfg=cfg; conn= DataSourceUtil.getConnection(cfg); } public <T> T getMapper (Class<T> daoInterfaceClass) { return (T)Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),conn)); } public void close () { try { if (conn!=null ){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
5.编写用于创建Dao接口代理对象的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.uestc.mybatis.sqlSession.proxy;import com.uestc.mybatis.cfg.Mapper;import com.uestc.mybatis.utils.Executor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.sql.Connection;import java.util.Map;public class MapperProxy implements InvocationHandler { private Map<String,Mapper> mappers; private Connection conn; public MapperProxy (Map<String,Mapper> mappers, Connection conn) { this .mappers=mappers; this .conn=conn; } public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { String methodName=method.getName(); String className=method.getDeclaringClass().getName(); String key=className+"." +methodName; Mapper mapper=mappers.get(key); if (mapper==null ){ throw new IllegalArgumentException("传入参数有误" ); } return new Executor().selectList(mapper,conn); } }
编写测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import com.uestc.mybatis.sqlSession.SqlSession;import com.uestc.mybatis.sqlSession.SqlSessionFactory;import com.uestc.mybatis.sqlSession.SqlSessionFactoryBuilder;import com.uestc.mybatis.io.Resources;import java.io.InputStream;import java.util.List;public class MybatisTest { public static void main (String[] args) throws Exception { InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(is); SqlSession session=factory.openSession(); IUserDao userDao=session.getMapper(IUserDao.class ) ; List<User> users=userDao.findAll(); for (User user:users){ System.out.println(user); } session.close(); is.close(); } }
基于注解方式定义Mybatis框架 1. 自定义@Select注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.uestc.mybatis.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention (RetentionPolicy.RUNTIME)@Target (ElementType.METHOD)public @interface Select { String value () ; }
2. 修改持久层接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.uestc.dao;import com.uestc.domain.User;import com.uestc.mybatis.annotations.Select;import java.util.List;public interface IUserDao { @Select ("select * from user" ) public List<User> findAll () ; }
3. 修改SqlMapConfig.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?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 > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="287216" > </property > </dataSource > </environment > </environments > <mappers > <mapper class ="com.uestc.dao.IUserDao" > </mapper > </mappers > </configuration >
自定义流程再分析
基于代理Dao实现CRUD操作 mybatis环境搭建步骤
创建maven工程
导入坐标
编写必要代码(实体类和持久层接口)
编写SqlMapConfig.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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 > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="jdbc" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="287216" > </property > </dataSource > </environment > </environments > <mappers > <mapper resource ="com/uestc/dao/IUserDao.xml" > </mapper > </mappers > </configuration >
编写映射配置文件
编写测试类
使用要求:
持久层接口和持久层接口的映射配置必须在相同的包下
持久层映射配置中mapper标签的namespace属性取值必须是持久层接口的全限定类名
SQL语句的配置标签<select>
,<insert>
,<delete>
,<update>
的id属性必须和持久层接口的方法名相同。
根据ID查询 1. 在持久层接口中添加findById方法 1 2 3 4 5 6 User findById (Integer userId) ;
2. 在用户的映射配置文件中配置 1 2 3 4 <select id ="findById" parameterType ="String" resultType ="com.uestc.domain.User" > select * from user where id=#{id} </select >
细节:
resultType
属性: 用于指定结果集的类型。
parameterType
属性:
用于指定传入参数的类型。 ql语句
中使用#{}
字符: 它代表占位符,相当于原来jdbc部分所学的?
,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容
决定的。
#{}
中内容的写法: 由于数据类型是基本类型,所以此处可以随意写
3. 在测试类添加测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindById () { User user=userDao.findById(50 ); System.out.println(user); }
保存操作 1. 在持久层接口中添加saveUser方法 1 2 3 4 5 void saveUser (User user) ;
2. 在用户的映射配置文件中配置 1 2 3 4 5 6 7 8 <insert id ="saveUser" parameterType ="com.uestc.domain.User" > <selectKey keyProperty ="id" keyColumn ="id" resultType ="int" order ="AFTER" > select last_insert_id(); </selectKey > insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday}) </insert >
细节:
parameterType
属性: 代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
sql语句中使用#{}字符
: 它代表占位符,相当于原来jdbc部分所学的?
,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容决定的。
#{}
中内容的写法: 由于我们保存方法的参数是 一个User对象,此处要写User对象中的属性名称。 它用的是ognl表达式。
ognl表达式
: 它是apache提供的一种表达式语言,全称是: Object Graphic Navigation Language
对象图导航语言 它是按照一定的语法格式来获取数据的。 语法格式就是使用 `#{对象.对象}的方式
#{user.username}
它会先去找user对象,然后在user对象中找到username属性,并调用getUsername()方法把值取出来。但是我们在parameterType属性上指定了实体类名称,所以可以省略user.而直接写username。
3. 添加测试类中的测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestSave () throws Exception { User user=new User(null ,"mybatis saveruser" ,"北京市顺义区" ,"男" ,new Date()); System.out.println("保存操作之前:" +user); userDao.saveUser(user); sqlSession.commit(); System.out.println("保存操作之后:" +user); }
细节 打开Mysql数据库发现并没有添加任何记录,原因是什么? 这一点和jdbc是一样的,我们在实现增删改时一定要去控制事务的提交,那么在mybatis中如何控制事务提交呢? 可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:
1 2 3 4 5 6 7 @After public void destroy () throws Exception { session.commit(); session.close(); in.close(); }
新增用户id的返回值 1 2 3 4 5 6 7 <insert id ="saveUser" parameterType ="USER" > <selectKey keyColumn ="id" keyProperty ="id" resultType ="int" > select last_insert_id(); </selectKey > insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert >
用户更新 1. 在持久层接口中添加更新方法 1 2 3 4 5 void updateUser (User user) ;
2. 在用户的映射配置文件中配置 1 2 3 4 5 <update id ="updateUser" parameterType ="com.uestc.domain.User" > update user set username=#{username},address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id} </update >
3. 加入更新的测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestUpdate () { User user=new User(50 ,"宝贝" ,"北京市海淀区" ,"女" ,new Date()); userDao.updateUser(user); sqlSession.commit(); } }
用户删除 1. 在持久层接口中添加删除方法 1 2 3 4 5 void deleteUser (Integer userId) ;
2. 在用户的映射配置文件中配置 1 2 3 4 <delete id ="deleteUser" parameterType ="int" > delete from user where id=#{id} </delete >
3. 加入删除的测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestDelete () { userDao.deleteUser(48 ); sqlSession.commit(); } }
用户模糊查询 1. 在持久层接口中添加模糊查询方法 1 2 3 4 5 6 List<User> findByName (String name) ;
2. 在用户的映射配置文件中配置 1 2 3 4 <select id ="findByName" parameterType ="String" resultType ="com.uestc.domain.User" > select * from user where username like #{name} </select >
3. 加入模糊查询的测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindByName () { List<User> users=userDao.findByName("%王%" ); for (User user:users){ System.out.println(user); } } }
模糊查询的另一种配置方式 第一步:修改SQL语句的配置,配置如下:
1 2 3 4 <select id ="findByName" parameterType ="string" resultType ="com.itheima.domain.User" > select * from user where username like '%${value}%' </select >
我们在上面将原来的#{}
占位符,改成了${value}
。注意如果用模糊查询的这种写法,那么${value}
的写法就是固定的,不能写成其它名字。
第二步:测试,如下:
1 2 3 4 5 6 7 8 9 10 11 @Test public void TestFindByName () { List<User> users=userDao.findByName("王" ); for (User user:users){ System.out.println(user); } }
#{}与${}的区别
#{}
表示一个占位符号 通过#{}
可以实现preparedStatement
向占位符中设置值,自动进行java类型和jdbc类型转换,#{}
可以有效防止sql注入。 #{}
可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}
表示拼接sql串 通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value
。
查询使用聚合函数 1.在持久层接口中添加count方法
2. 在用户的映射配置文件中配置 1 2 3 4 <select id ="findTotal" resultType ="int" > select count(id) from user </select >
3. 加入聚合查询的测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindTotal () { int total = userDao.findTotal(); System.out.println(total); } }
总结 IUserDao接口类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { List<User> findAll () ; void saveUser (User user) ; void updateUser (User user) ; void deleteUser (Integer userId) ; User findById (Integer userId) ; List<User> findByName (String name) ; int findTotal () ; }
IUserDao.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?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.uestc.dao.IUserDao" > <select id ="findAll" resultType ="com.uestc.domain.User" > select * from user </select > <insert id ="saveUser" parameterType ="com.uestc.domain.User" > <selectKey keyProperty ="id" keyColumn ="id" resultType ="int" order ="AFTER" > select last_insert_id(); </selectKey > insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday}) </insert > <update id ="updateUser" parameterType ="com.uestc.domain.User" > update user set username=#{username},address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id} </update > <delete id ="deleteUser" parameterType ="int" > delete from user where id=#{id} </delete > <select id ="findById" parameterType ="String" resultType ="com.uestc.domain.User" > select * from user where id=#{id} </select > <select id ="findByName" parameterType ="String" resultType ="com.uestc.domain.User" > select * from user where username like '%${value}%' </select > <select id ="findTotal" resultType ="int" > select count(id) from user </select > </mapper >
测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () throws Exception { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } @Test public void TestSave () throws Exception { User user=new User(null ,"mybatis saveruser" ,"北京市顺义区" ,"男" ,new Date()); System.out.println("保存操作之前:" +user); userDao.saveUser(user); sqlSession.commit(); System.out.println("保存操作之后:" +user); } @Test public void TestUpdate () { User user=new User(50 ,"宝贝" ,"北京市海淀区" ,"女" ,new Date()); userDao.updateUser(user); sqlSession.commit(); } @Test public void TestDelete () { userDao.deleteUser(48 ); sqlSession.commit(); } @Test public void TestFindById () { User user=userDao.findById(50 ); System.out.println(user); } @Test public void TestFindByName () { List<User> users=userDao.findByName("王" ); for (User user:users){ System.out.println(user); } } @Test public void TestFindTotal () { int total = userDao.findTotal(); System.out.println(total); } }
Mybatis与JDBC编程的比较
数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决: 在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决: 将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数对应。
解决: Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决: Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
分析代理dao的执行过程
Mybatis的参数深入 Mybatis的参数 parameterType配置参数 我们在上一章节中已经介绍了SQL语句传参,使用标签的parameterType
属性来设定。该属性的取值可以是基本类型
,引用类型
(例如:String类型),还可以是实体类类型
(POJO类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类
作为参数传递。
注意事项 基本类型
和String
我们可以直接写类型名称,也可以使用包名.类名
的方式,例如:java.lang.String。
实体类类型
,目前我们只能使用全限定类名
。 究其原因,是mybaits在加载时已经把常用的数据类型注册了别名
,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。在今天课程的最后一个章节中将讲解如何注册实体类的别名。
传递pojo对象 mybatis使用ognl表达式
解析对象字段的值,#{}
或者${}
括号中的值为pojo属性名称
OGNL表达式 Object Graphic Navigation Language(对象导航语言)
它是通过对象的取值方法来获取数据再写法上把get给省略了
比如获取用户的名称
在类中的写法:user.getUsername();
OGNL表达式写法:user.Username
mybatis中为什么能直接写username,而不用写user.username呢
因为在parameteType中已经提供了属性所属的类,所以此时不需要写对象名
传递pojo包装对象 开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
Pojo类中包含pojo。
需求:根据用户名查询用户信息,查询条件放到QueryVo的user属性中。
1. 编写QueryVo 1 2 3 4 5 6 7 8 9 10 package com.uestc.domain;public class QueryVo { private User user; public User getUser () { return user; } public void setUser (User user) { this .user = user; } }
2. 编写持久层接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.uestc.dao;import com.uestc.domain.QueryVo;import com.uestc.domain.User;import java.util.List;public interface IUserDao { int findUserByVo (QueryVo vo) ; }
3. 持久层接口的映射文件 1 2 3 4 <!--根据queryVo的条件查询用户--> <select id="findUserByVo" parameterType="com.uestc.domain.QueryVo" > select * from user where id=#{user.username} </select>
4. 测试包装类作为参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.uestc.test;import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import com.uestc.dao.IUserDao;import com.uestc.domain.QueryVo;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindByVo () { QueryVo queryVo=new QueryVo(); User u=new User(); queryVo.setUser(u); u.setUsername("王" ); List<User> users=userDao.findUserByVo(queryVo); for (User user:users){ System.out.println(user); } } }
Mybatis的输出结果封装 resultType配置结果类型 resultType属性可以指定结果集的类型,它支持基本类型
和实体类类型
。 我们在前面的CRUD案例中已经对此属性进行过应用了。
需要注意的是,它和parameterType一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)
同时,当是实体类名称是,还有一个要求,实体类
中的属性名称
必须和查询语句
中的列名保持一致
,否则无法实现封装
。
特殊情况示例 此时的实体类属性和数据库表的列名已经不一致了
修改实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer userId; private String userName; private String userAddress; private String userSex; private Date userBirthday; public User () {}; public User (Integer userId, String userName, String userAddress, String userSex, Date userBirthday) { this .userId = userId; this .userName = userName; this .userAddress = userAddress; this .userSex = userSex; this .userBirthday = userBirthday; } public Integer getUserId () { return userId; } public void setUserId (Integer userId) { this .userId = userId; } public String getUserName () { return userName; } public void setUserName (String userName) { this .userName = userName; } public String getUserAddress () { return userAddress; } public void setUserAddress (String userAddress) { this .userAddress = userAddress; } public String getUserSex () { return userSex; } public void setUserSex (String userSex) { this .userSex = userSex; } public Date getUserBirthday () { return userBirthday; } public void setUserBirthday (Date userBirthday) { this .userBirthday = userBirthday; } @Override public String toString () { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + ", userAddress='" + userAddress + '\'' + ", userSex='" + userSex + '\'' + ", userBirthday=" + userBirthday + '}' ; } }
Dao接口: 1 2 3 4 5 List<User> findAll () ;
映射配置: 1 2 3 4 <select id ="findAll" resultType ="com.uestc.domain.User" > select * from user </select >
测试查询结果 1 2 3 4 5 6 7 8 @Test public void TestFindAll () throws Exception { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } }
为什么名称会有值呢? 因为:mysql在windows系统中不区分大小写!
修改映射配置 使用别名查询 1 2 3 4 <select id ="findAll" resultType ="com.uestc.domain.User" > select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user </select >
如果我们的查询很多,都使用别名的话写起来岂不是很麻烦
resultMap结果类型 resultMap标签可以建立查询的列名
和实体类的属性名称
不一致`时建立对应关系。从而实现封装。
在select标签中使用resultMap属性指定引用即可。同时resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
定义resultMap 1 2 3 4 5 6 7 8 9 10 <resultMap id ="userMap" type ="com.uestc.domain.User" > <id property ="userId" column ="id" > </id > <result property ="userName" column ="username" > </result > <result property ="userAddress" column ="address" > </result > <result property ="userSex" column ="sex" > </result > <result property ="userBirthday" column ="birthday" > </result > </resultMap >
映射配置 1 2 3 4 <select id ="findAll" resultMap ="userMap" > select * from user </select >
测试结果
Mybatis传统DAO层开发 使用Mybatis开发Dao,通常有两个方法,即原始Dao开发方式
和Mapper接口代理开发方式
。而现在主流的开发方式是接口代理开发方式,这种方式总体上更加简便。我们的课程讲解也主要以接口代理开发方式为主。在第二章节已经给大家介绍了基于代理方式的dao开发,现在给大家介绍一下基于传统编写Dao实现类的开发方式。
1. 编写用户实体类User 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; public User () {}; public User (Integer id, String username, String address, String sex, Date birthday) { this .id = id; this .username = username; this .address = address; this .sex = sex; this .birthday = birthday; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "rId=" + id + ", name='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}' ; } }
2. 编写持久层Dao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { List<User> findAll () ; void saveUser (User user) ; void updateUser (User user) ; void deleteUser (Integer userId) ; User findById (Integer userId) ; List<User> findByName (String name) ; int findTotal () ; }
3. 编写持久层Dao实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 package com.uestc.dao.impl;import com.uestc.dao.IUserDao;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import java.util.List;public class UserDaoImpl implements IUserDao { private SqlSessionFactory factory; public UserDaoImpl (SqlSessionFactory factory) { this .factory=factory; } public List<User> findAll () { SqlSession sqlSession=factory.openSession(); List<User> users=sqlSession.selectList("com.uestc.dao.IUserDao.findAll" ); sqlSession.close(); return users; } public void saveUser (User user) { SqlSession sqlSession=factory.openSession(); sqlSession.insert("com.uestc.dao.IUserDao.saveUser" ,user); sqlSession.commit(); sqlSession.close(); } public void updateUser (com.uestc.domain.User user) { SqlSession sqlSession=factory.openSession(); sqlSession.update("com.uestc.dao.IUserDao.updateUser" ,user); sqlSession.commit(); sqlSession.close(); } public void deleteUser (Integer userId) { SqlSession sqlSession=factory.openSession(); sqlSession.delete("com.uestc.dao.IUserDao.deleteUser" ,userId); sqlSession.commit(); sqlSession.close(); } public User findById (Integer userId) { SqlSession sqlSession=factory.openSession(); User user=sqlSession.selectOne("com.uestc.dao.IUserDao.findById" ,userId); sqlSession.close(); return user; } public List<User> findByName (String name) { SqlSession sqlSession=factory.openSession(); List<User> users=sqlSession.selectList("com.uestc.dao.IUserDao.findByName" ,name); sqlSession.close(); return users; } public int findTotal () { SqlSession sqlSession=factory.openSession(); int count=sqlSession.selectOne("com.uestc.dao.IUserDao.findTotal" ); sqlSession.close(); return count; } }
4. 编写持久层映射配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?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.uestc.dao.IUserDao" > <select id ="findAll" resultType ="com.uestc.domain.User" > select * from user </select > <insert id ="saveUser" parameterType ="com.uestc.domain.User" > <selectKey keyProperty ="id" keyColumn ="id" resultType ="int" order ="AFTER" > select last_insert_id(); </selectKey > insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday}) </insert > <update id ="updateUser" parameterType ="com.uestc.domain.User" > update user set username=#{username},address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id} </update > <delete id ="deleteUser" parameterType ="int" > delete from user where id=#{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.uestc.domain.User" > select * from user where id=#{id} </select > <select id ="findByName" parameterType ="String" resultType ="com.uestc.domain.User" > select * from user where username like '%${value}%' </select > <select id ="findTotal" resultType ="int" > select count(id) from user </select > </mapper >
5. 编写测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.dao.impl.UserDaoImpl;import com.uestc.domain.User;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.apache.ibatis.io.Resources;import java.io.InputStream;import java.util.Date;import java.util.List;public class MybatisTest { private InputStream in; private IUserDao userDao; @Before public void init () throws Exception { in=Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in); userDao=new UserDaoImpl(factory); } @After public void destory () throws Exception { in.close(); } @Test public void TestFindAll () throws Exception { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } @Test public void TestSave () throws Exception { User user=new User(null ,"小可爱" ,"四川大竹" ,"女" ,new Date()); System.out.println("保存操作之前:" +user); userDao.saveUser(user); System.out.println("保存操作之后:" +user); } @Test public void TestUpdate () { User user=new User(51 ,"大宝贝" ,"成都" ,"女" ,new Date()); userDao.updateUser(user); } @Test public void TestDelete () { userDao.deleteUser(54 ); } @Test public void TestFindById () { User user=userDao.findById(46 ); System.out.println(user); } @Test public void TestFindByName () { List<User> users=userDao.findByName("王" ); for (User user:users){ System.out.println(user); } } @Test public void TestFindTotal () { int total = userDao.findTotal(); System.out.println(total); } }
分析编写dao实现类Mybatis的执行过程
SqlMapConfig.xml配置文件 SqlMapConfig.xml中配置的内容和顺序
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
properties(属性) 在使用properties标签配置时,我们可以采用两种方式指定属性配置
标签内部配置连接数据库的信息
通过属性引用外部配置文件信息
1. 标签内部配置 1 2 3 4 5 6 7 <properties > <property name ="jdbc.driver" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbc.url" value ="jdbc:mysql://localhost:3306/mybatis" > </property > <property name ="jdbc.username" value ="root" > </property > <property name ="jdbc.password" value ="287216" > </property > </properties >
2. 在classpath下定义db.properties文件 1 2 3 4 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/mybatis jdbc.username =root jdbc.password =287216
并在properties
标签配置
1 2 3 4 5 6 7 8 9 10 11 12 <properties resource ="db.properties" > </properties >
使用 此时我们的dataSource
标签就变成了引用上面的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="jdbc" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" > </property > <property name ="url" value ="${jdbc.url}" > </property > <property name ="username" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </dataSource > </environment > </environments >
typeAliases(类型别名) 在前面我们讲的Mybatis支持的默认别名,我们也可以采用自定义别名方式来开发。
自定义别名 在SqlMapConfig.xml
中配置:
1 2 3 4 5 6 7 8 <typeAliases > <typeAlias type ="com.uestc.domain.User" alias ="user" > </typeAlias > <package name ="com.uestc.domain" > </package > </typeAliases >
mappers(映射器) 1. <mapper resource=" " />
使用相对于类路径的资源 ,如:
1 <mapper resource ="com/itheima/dao/IUserDao.xml" />
2. <mapper class=" " />
使用mapper接口类路径, 如
1 2 <mapper class ="com.itheima.dao.UserDao" />
3. <package name=""/>
注册指定包下的所有mapper接口,如
1 2 <package name ="cn.itcast.mybatis.mapper" />
mybatis执行过程分析
Mybatis连接池与事务深入 我们在前面的WEB课程中也学习过类似的连接池技术,而在Mybatis中也有连接池技术,但是它采用的是自己的连接池技术。在Mybatis的SqlMapConfig.xml配置文件中,通过<dataSource type="pooled">
来实现Mybatis中连接池的配置。
Mybatis中的连接池 我们在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间
Mybatis中我们将它的数据源dataSource分为以下几类
可以看出Mybatis将它自己的数据源分为三类:
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源
JNDI 使用JNDI实现的数据源
mybaits连接池提供了3种方式的配置
配置的位置 :主配置文件SqlMapConfig.xml
中的dataSource
type
属性的取值
POOLED
采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED
采用传统的获取连接的方式,虽然也实现了Javax.sql.DataSource接口,但是并没与使用池技术思想
JNDI
采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器锁能拿到的DataSource是不一样的,注意如果不是web或者maven的war工程,是不能使用的
tomcat服务器,采用连接池就是dbcp连接池
Mybatis中连接的获取过程分析(一) 当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。
1 2 3 4 5 6 7 @Test public void testSql () throws Exception { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); List<User> list = sqlSession.selectList("findUserById" ,41 ); System.out.println(list.size()); }
只有当第4句sqlSession.selectList("findUserById")
,才会触发MyBatis在底层执行下面这个方法来创建java.sql.Connection
对象
在PooledDataSource中找到如下popConnection()
方法,如下所示:
分析源代码,得出PooledDataSource工作原理如下
下面是连接获取的源代码:
最后我们可以发现,真正连接打开的时间点,只是在我们执行SQL语句时,才会进行。其实这样做我们也可以进一步发现,数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中
Mybatis中pooled的过程(二)
Mybatis的事务控制 Mybatis中事务提交方式 Mybatis中事务的提交方式,本质上就是调用JDBC的setAutoCommit()
来实现事务控制。
我们运行之前所写的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void testSaveUser () throws Exception { User user = new User(); user.setUsername("mybatis user09" ); System.out.println(res); System.out.println(user.getId()); } @Before public void init () throws Exception { in = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); factory = builder.build(in); session = factory.openSession(); userDao = session.getMapper(IUserDao.class ) ; } @After public void destroy () throws Exception { session.commit(); session.close(); in.close(); }
观察在它在控制台输出的结果:
这是我们的Connection的整个变化过程,通过分析我们能够发现之前的CUD操作过程中,我们都要手动进行事务的提交,原因是setAutoCommit()方法,在执行时它的值被设置为false了,所以我们在CUD操作中,必须通过sqlSession.commit()方法来执行提交操作
Mybatis自动提交事务的设置 为什么CUD过程中必须使用sqlSession.commit()提交事务?
主要原因就是在连接池中取出的连接,都会将调用connection.setAutoCommit(false)方法,这样我们就必须使用sqlSession.commit()方法,相当于使用了JDBC中的connection.commit()方法实现事务提交。
1 2 3 4 5 6 7 8 9 10 11 12 @Before public void init () throws Exception { in = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); factory = builder.build(in); userDao = session.getMapper(IUserDao.class ) ; }
所对应的DefaultSqlSessionFactory类的源代码:
Mybatis的动态SQL语句 动态SQL之<if>
标签 我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到
1. 持久层Dao接口 1 2 3 4 5 List<User> findUserByCondition (User user) ;
2,持久层Dao映射配置 1 2 3 4 5 6 7 8 9 10 <select id ="findUserByCondition" resultMap ="userMap" parameterType ="user" > select * from user where 1=1 <if test ="userName!=null" > and username=#{userName} </if > <if test ="userSex!=null" > and sex=#{userSex} </if > </select >
注意:<if>
标签的test属性中写的是对象的属性名,如果是包装类的对象要使用OGNL表达式的写法。 另外要注意where 1=1 的作用~!
3.测试
1 2 3 4 5 6 7 8 9 10 @Test public void TestFindByCondition () { User user=new User(); user.setUserName("老王" ); user.setUserSex("女" ); List<User> users=userDao.findUserByCondition(user); for (User u:users){ System.out.println(u); } }
动态SQL之<where>
标签 为了简化上面where 1=1的条件拼装,我们可以采用<where>
标签来简化开发。
1 2 3 4 5 6 7 8 9 10 11 12 <select id ="findUserByCondition" resultMap ="userMap" parameterType ="user" > select * from user <where > <if test ="userName!=null" > and username=#{userName} </if > <if test ="userSex!=null" > and sex=#{userSex} </if > </where > </select >
动态标签之<foreach>
标签 SQL语句:
1 select 字段`from user where id in (?)
<foreach>
标签用于遍历集合
它的属性:
collection
:代表要遍历的集合元素,注意编写时不要写#{}
open
:代表语句的开始部分
close
:代表结束部分
item
:代表遍历集合的每个元素,生成的变量名
sperator
:代表分隔符
需求 传入多个id查询用户信息,用下边两个sql实现:
1 2 SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id =16 ) SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10 ,89 ,16 )
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。 这样我们将如何进行参数的传递?
步骤: 1. 在QueryVo中加入一个List集合用于封装参数 1 2 3 4 5 6 7 8 9 10 11 package com.uestc.domain;import java.util.List;public class QueryVo { private List<Integer> ids; public List<Integer> getIds () { return ids; } public void setIds (List<Integer> ids) { this .ids = ids; } }
2. 在持久层Dao接口中添加findUserInIds方法 1 2 3 4 5 6 List<User> findUserInIds (QueryVo queryVo) ;
3. 持久层Dao映射配置 1 2 3 4 5 6 7 8 9 10 11 <select id ="findUserInIds" resultMap ="userMap" parameterType ="queryVo" > select * from user <where > <if test ="ids!=null and ids.size()>0" > <foreach collection ="ids" open ="and id in(" close =")" item ="id" separator ="," > #{id} </foreach > </if > </where > </select >
4. 编写测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void TestFindInIds () { QueryVo queryVo=new QueryVo(); List<Integer> list=new ArrayList<Integer>(); list.add(41 ); list.add(42 ); list.add(43 ); queryVo.setIds(list); List<User> users=userDao.findUserInIds(queryVo); for (User u:users){ System.out.println(u); } }
Mybatis中简化编写的SQL片段 Sql中可将重复的sql提取出来,使用时用include
引用即可,最终达到sql重用的目的
步骤 1.定义代码片段 1 2 3 4 <!--抽取重复的sql--> <sql id="defaultSql"> select * from user </sql >
2. 引用代码片段 1 2 3 4 <!-- 查询所有--> <select id="findAll" resultMap="userMap"> <include refid="defaultSql"></include> </select>
Mybatis 多表查询之一对多 表之间的关系有多种:一对多
、多对一
、一对一
和多对多
举例:
用户和订单就是一对多:一个用户可以下多个订单
订单和用户就是多对一:多个订单属于同一个用户
人和身份证就是一对一:一个人只能有一个身份证号,而一个身份证号只能属于一个人
老师和学生之间就是多对多:一个学生可以被多个老师教,一个老师可以教多个学生
特例:如果拿出每一个订单,他都只能属于一个用户,所有Mybatis就把多对一看成了一对一。
示例:用户和账户 一个用户可以有多个账户,一个账户只能属于一个用户
步骤:
建立两张表:用户表,账户表
让用户表和账户表之间具备一对多的关系:需要在账户表中添加外键
建立两个实体类:用户实体类和账户实体类
让用户和账户的实体类能体现出一对多的关系
建立两个配置文件:用户的配置文件和账户的配置文件
当我们查询用户时,可以同时得到用户下所包含的账户信息
当我们查询账户时,可以得到账户的所属的用户信息
数据准备
建立账户表:account
1 2 3 4 5 6 7 8 9 10 CREATE TABLE `account` ( `ID` int (11 ) NOT NULL COMMENT '编号' , `UID` int (11 ) default NULL COMMENT '用户编号' , `MONEY` double default NULL COMMENT '金额' , PRIMARY KEY (`ID` ), KEY `FK_Reference_8` (`UID` ), CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID` ) REFERENCES `user` (`id` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8; insert into `account` (`ID` ,`UID` ,`MONEY` ) values (1 ,41 ,1000 ),(2 ,45 ,1000 ),(3 ,41 ,2000 );
建立用户表
1 2 3 4 5 6 7 8 CREATE TABLE `user` ( `id` int (11 ) NOT NULL auto_increment, `username` varchar (32 ) NOT NULL COMMENT '用户名称' , `birthday` datetime default NULL COMMENT '生日' , `sex` char (1 ) default NULL COMMENT '性别' , `address` varchar (256 ) default NULL COMMENT '地址' , PRIMARY KEY (`id` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8;
一对一查询(多对一) 需求
查询所有账户信息,关联查询下单用户信息。
注意: 因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如 果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
方法一: 1. 定义账户信息的实体类 和 用户实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.uestc.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private Integer uid; private Double money; public Account () {}; public Account (Integer id, Integer uid, Double money) { this .id = id; this .uid = uid; this .money = money; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public Integer getUid () { return uid; } public void setUid (Integer uid) { this .uid = uid; } public Double getMoney () { return money; } public void setMoney (Double money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + '}' ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; public User () {}; public User (Integer id, String username, String address, String sex, Date birthday) { this .id = id; this .username = username; this .address = address; this .sex = sex; this .birthday = birthday; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsernamesername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}' ; } }
2. 编写Sql语句 1 2 3 4 5 6 7 8 select a.*, u.username, u.address from user u, account a where u.id=a.uid;
在MySQL中测试的查询结果如下:
3. 定义AccountUser类 为了能够封装上面SQL语句的查询结果,定义 AccountCustomer类中要包含账户信息同时还要包含用户信息,所以我们要在定义AccountUser类时可以继承
User类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.uestc.domain;public class AccountUser extends Account { private String username; private String address; public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return super .toString()+"AccountUser{" + "username='" + username + '\'' + ", address='" + address + '\'' + '}' ; } }
4. 定义账户的持久层Dao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.uestc.dao;import com.uestc.domain.Account;import com.uestc.domain.AccountUser;import java.util.List;public interface IAccountDao { List<Account> findAll () ; List<AccountUser> findAllAcount () ; }
5. 定义AccountDao.xml文件中的查询配置信息 1 2 3 4 5 6 7 8 9 10 <?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.uestc.dao.IAccountDao" > <select id ="findAllAcount" resultType ="AccountUser" > select a.*, u.username, u.address from user u, account a where u.id=a.uid </select > </mapper >
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型returnType的值设置为AccountUser类型,这样就可以接收账户信息和用户信息了。
6. 创建AccountTest测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.uestc.test;import com.uestc.dao.IAccountDao;import com.uestc.dao.IUserDao;import com.uestc.domain.Account;import com.uestc.domain.AccountUser;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class AccountTest { private InputStream in; private SqlSession sqlSession; private IAccountDao accountDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); accountDao=sqlSession.getMapper(IAccountDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAllAcount () { List<AccountUser> accountUsers =accountDao.findAllAcount(); for (AccountUser accountUser: accountUsers) { System.out.println(accountUser); } } }
方式二 使用resultMap
,定义专门的resultMap用于映射一对一查询结果。 通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的。
1. 修改Account类 在Account类中加入User类的对象作为Account类的一个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.uestc.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private Integer uid; private Double money; private User user; public User getUser () { return user; } public void setUser (User user) { this .user = user; } public Account () {}; public Account (Integer id, Integer uid, Double money) { this .id = id; this .uid = uid; this .money = money; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public Integer getUid () { return uid; } public void setUid (Integer uid) { this .uid = uid; } public Double getMoney () { return money; } public void setMoney (Double money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + '}' ; } }
2. 修改AccountDao接口中的方法 1 2 3 4 5 6 7 8 9 10 11 12 package com.uestc.dao;import com.uestc.domain.Account;import com.uestc.domain.AccountUser;import java.util.List;public interface IAccountDao { List<Account> findAll () ; }
注意:第二种方式,将返回值改 为了Account类型。 因为Account类中包含了一个User类的对象,它可以封装账户所对应的用户信息。
3. 重新定义AccountDao.xml文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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.uestc.dao.IAccountDao" > <resultMap id ="accountUserMap" type ="account" > <id property ="id" column ="aid" > </id > <result property ="uid" column ="uid" > </result > <result property ="money" column ="money" > </result > <association property ="user" javaType ="user" > <id property ="id" column ="id" > </id > <result property ="username" column ="username" > </result > <result property ="address" column ="address" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > </association > </resultMap > <select id ="findAll" resultMap ="accountUserMap" > select u.*, a.id as aid, a.money from user u, account a where u.id=a.uid; </select > </mapper >
4. 在AccountTest类中加入测试方法 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void TestFindAll () { List<Account> accounts =accountDao.findAll(); for (Account account: accounts) { System.out.println("-------每个account的信息------" ); System.out.println(account); System.out.println(account.getUser()); } }
一对多查询 需求:
查询所有用户信息及用户关联的账户信息。
分析: 用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
1. 编写SQL语句 1 2 3 4 5 6 7 8 select u.*, a.id as aid, a.uid, a.money from user u left join account a on u.id=a.uid;
测试该SQL语句在MySQL客户端工具的查询结果如下
2. User类加入List<Account>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.uestc.domain;import java.io.Serializable;import java.util.Date;import java.util.List;public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; private List<Account> accounts; public List<Account> getAccounts () { return accounts; } public void setAccounts (List<Account> accounts) { this .accounts = accounts; } public User () {}; public User (Integer id, String username, String address, String sex, Date birthday) { this .id = id; this .username = username; this .address = address; this .sex = sex; this .birthday = birthday; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}' ; } }
3. 用户持久层Dao接口中加入查询方法 1 2 3 4 5 List<User> findAll () ;
4. 用户持久层Dao映射文件配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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.uestc.dao.IUserDao" > <resultMap id ="usermap" type ="user" > <id property ="id" column ="id" > </id > <result property ="username" column ="username" > </result > <result property ="address" column ="address" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > <collection property ="accounts" ofType ="Account" > <id property ="id" column ="aid" > </id > <result property ="uid" column ="uid" > </result > <result property ="money" column ="money" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="usermap" > select u.*, a.id as aid, a.uid, a.money from user u left join account a on u.id=a.uid </select > </mapper >
collection
部分定义了用户关联的账户信息。表示关联查询结果集
5. 测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () { List<User> users=userDao.findAll(); for (User user: users){ System.out.println("每个用户所有的账户信息-----" ); System.out.println(user); System.out.println(user.getAccounts()); } } }
Mybatis 多表查询之多对多 1. 实现Role 到User 多对多 用户与角色的关系模型 用户与角色的多对多关系模型如下:
步骤:
建立两张表:用户表,角色表
让用户表和角色表之间具备多对多的关系:需要使用中间表,中间表中分别包含指向两张表主键的外键
建立两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出多对多的关系
各自包含对方的一个集合引用
建立两个配置文件:用户的配置文件和角色的配置文件
当我们查询用户时,可以同时得到用户下所包含的角色信息
当我们查询角色时,可以得到角色的所赋予的用户信息
数据准备 在MySQL 数据库中添加角色表,用户角色的中间表。
角色表:
1 2 3 4 5 6 7 8 9 10 11 12 13 CREATE TABLE `role` ( `ID` int (11 ) NOT NULL COMMENT '编号' , `ROLE_NAME` varchar (30 ) default NULL COMMENT '角色名称' , `ROLE_DESC` varchar (60 ) default NULL COMMENT '角色描述' , PRIMARY KEY (`ID` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8; insert into `role` (`ID` ,`ROLE_NAME` ,`ROLE_DESC` ) values (1 ,'院长' ,'管理整个学院' ), (2 ,'总裁' ,'管理整个公司' ), (3 ,'校长' ,'管理整个学校' );
用户角色中间表:
1 2 3 4 5 6 7 8 9 10 11 12 13 CREATE TABLE `user_role` ( `UID` int (11 ) NOT NULL COMMENT '用户编号' , `RID` int (11 ) NOT NULL COMMENT '角色编号' , PRIMARY KEY (`UID` ,`RID` ), KEY `FK_Reference_10` (`RID` ), CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID` ) REFERENCES `role` (`ID` ), CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID` ) REFERENCES `user` (`id` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8; insert into `user_role` (`UID` ,`RID` ) values (41 ,1 ),(45 ,1 ),(41 ,2 );
1. 业务要求及实现SQL 需求:
实现查询所有对象并且加载它所分配的用户信息。
分析: 查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE表)才能关联到用户信息。
下面是实现的SQL语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 select r.*, u.id as uid, u.username, u.address, u.sex, u.birthday from role r left join user_role ur on (ur.rid=r.id)left join user u on (ur.uid=u.id);
2. 编写角色实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.uestc.domain;import java.io.Serializable;import java.util.List;public class Role implements Serializable { private Integer roleId; private String roleName; private String roleDesc; private List<User> users; public List<User> getUsers () { return users; } public void setUsers (List<User> users) { this .users = users; } public Integer getRoleId () { return roleId; } public void setRoleId (Integer roleId) { this .roleId = roleId; } public String getRoleName () { return roleName; } public void setRoleName (String roleName) { this .roleName = roleName; } public String getRoleDesc () { return roleDesc; } public void setRoleDesc (String roleDesc) { this .roleDesc = roleDesc; } @Override public String toString () { return "Role{" + "roleId=" + roleId + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}' ; } }
3. 编写Role持久层接口 1 2 3 4 5 6 7 8 9 10 package com.uestc.dao;import com.uestc.domain.Role;import java.util.List;public interface IRoleDao { List<Role> findAll () ; }
4. 编写映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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.uestc.dao.IRoleDao" > <resultMap id ="rolemap" type ="role" > <id property ="roleId" column ="id" > </id > <result property ="roleName" column ="role_name" > </result > <result property ="roleDesc" column ="role_desc" > </result > <collection property ="users" ofType ="user" > <id property ="id" column ="uid" > </id > <result property ="username" column ="username" > </result > <result property ="address" column ="address" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="rolemap" > select r.*, u.id as uid, u.username, u.address, u.sex, u.birthday from role r left join user_role ur on ur.rid=r.id left join user u on ur.uid=u.id </select > </mapper >
5. 编写测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.uestc.test;import com.uestc.dao.IRoleDao;import com.uestc.dao.IUserDao;import com.uestc.domain.Role;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class RoleTest { private InputStream in; private SqlSession sqlSession; private IRoleDao roleDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); roleDao=sqlSession.getMapper(IRoleDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () { List<Role> roles=roleDao.findAll(); for (Role role: roles ) { System.out.println("----每个角色赋予的用户信息" ); System.out.println(role); System.out.println(role.getUsers()); } } }
2. 实现User到Role的多对多 从User出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为User与Role的多对多关系,可以被拆解成两个一对多关系来实现。
1. 业务要求及实现SQL 1 2 3 4 5 6 7 8 9 select u.*, r.id as rid, r.role_name, r.role_desc from user u left join user_role ur on u.id=ur.uidleft join role r on ur.rid=r.id;
2. 编写用户实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.uestc.domain;import java.io.Serializable;import java.util.Date;import java.util.List;public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; private List<User> roles; public List<User> getRoles () { return roles; } public void setRoles (List<User> roles) { this .roles = roles; } public User () {}; public User (Integer id, String username, String address, String sex, Date birthday) { this .id = id; this .username = username; this .address = address; this .sex = sex; this .birthday = birthday; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}' ; } }
3. 编写用户持久层接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { List<User> findAll () ; User findById (Integer userId) ; }
4. 编写映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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.uestc.dao.IUserDao" > <resultMap id ="usermap" type ="user" > <id property ="id" column ="id" > </id > <result property ="username" column ="username" > </result > <result property ="address" column ="address" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > <collection property ="roles" ofType ="role" > <id property ="roleId" column ="rid" > </id > <result property ="roleName" column ="role_name" > </result > <result property ="roleDesc" column ="role_desc" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="usermap" > select u.*, r.id as rid, r.role_name, r.role_desc from user u left join user_role ur on u.id=ur.uid left join role r on ur.rid=r.id; </select > </mapper >
5. 编写测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.Role;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () { List<User> users=userDao.findAll(); for (User user: users ) { System.out.println("----每个用户的角色信息" ); System.out.println(user); System.out.println(user.getRoles()); } } }
JNDI数据源
JNDI:Java Naming and Directory Interface。是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。
步骤: 1. 创建Maven的war工程并导入坐标
注意java, resouces文件需要自己创建,并make source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="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 > com.uestc</groupId > <artifactId > day03jndimybatis</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > <name > day03jndimybatis Maven Webapp</name > <url > http://www.example.com</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.7</maven.compiler.source > <maven.compiler.target > 1.7</maven.compiler.target > </properties > <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.5</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.0</version > </dependency > </dependencies > <build > <finalName > day03jndimybatis</finalName > <pluginManagement > <plugins > <plugin > <artifactId > maven-clean-plugin</artifactId > <version > 3.1.0</version > </plugin > <plugin > <artifactId > maven-resources-plugin</artifactId > <version > 3.0.2</version > </plugin > <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > 2.22.1</version > </plugin > <plugin > <artifactId > maven-war-plugin</artifactId > <version > 3.2.2</version > </plugin > <plugin > <artifactId > maven-install-plugin</artifactId > <version > 2.5.2</version > </plugin > <plugin > <artifactId > maven-deploy-plugin</artifactId > <version > 2.8.2</version > </plugin > </plugins > </pluginManagement > </build > </project >
3. 在META-INF目录中建立一个名为context.xml的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8"?> <Context > <Resource name ="jdbc/eesy_mybatis" type ="javax.sql.DataSource" auth ="Container" maxActive ="20" maxWait ="10000" maxIdle ="5" username ="root" password ="287216" driverClassName ="com.mysql.jdbc.Driver" url ="jdbc:mysql://localhost:3306/mybatis" /> </Context >
4. 修改SqlMapConfig.xml中的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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 > <typeAliases > <package name ="com.uestc.domain" > </package > </typeAliases > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="JNDI" > <property name ="data_source" value ="java:comp/env/jdbc/eesy_mybatis" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/uestc/dao/IUserDao.xml" /> </mappers > </configuration >
5. 新建index.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <%@ page import ="java.io.InputStream" %> <%@ page import ="org.apache.ibatis.io.Resources" %> <%@ page import ="org.apache.ibatis.session.SqlSessionFactoryBuilder" %> <%@ page import ="org.apache.ibatis.session.SqlSessionFactory" %> <%@ page import ="org.apache.ibatis.session.SqlSession" %> <%@ page import ="com.uestc.dao.IUserDao" %> <%@ page import ="com.uestc.domain.User" %> <%@ page import ="java.util.List" %> <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %> <html> <body> <h2>helloword</h2> <% InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(is); SqlSession sqlSession=factory.openSession(); IUserDao userDao=sqlSession.getMapper(IUserDao.class ) ; List<User> users=userDao.findAll(); for (User user:users){ System.out.println(user); } sqlSession.close(); is.close(); %> </body> </html>
6. 部署启动tomcat
Mybatis延迟加载策略 问题:在一对多中,当我们有一个用户,它有100个账户
在查询用户的时候,要不要把关联的账户信息查出来?
在查询账户的时候,要不要把关联的用户信息查出来?
答:
在查询用户的时候,用户下的账户信息应该:什么时候使用,就什么时候查询
在查询账户的时候,账户下的所属用户信息应该时随着账户查询时一起查询出来
什么是延迟加载?
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
什么是立即加载
不管用不用,只要一调用方法,马上发起查询
选择策略 在对应的四种表关系中:一对多、多对一、一对一和多对多
多对一和一对一:通常情况下我们都是采用立即加载
一对多,多对多:通常情况下我们都是采用延迟加载
实现需求 查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
使用assocation实现延迟加载 需求: 查询账户信息同时查询用户信息。
1. 账户的持久层DAO接口 1 2 3 4 5 6 7 8 9 10 package com.uestc.dao;import com.uestc.domain.Account;import java.util.List;public interface IAccountDao { List<Account> findAll () ; }
2. 账户的持久层映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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.uestc.dao.IAccountDao" > <resultMap id ="accountmap" type ="account" > <id property ="id" column ="id" > </id > <result property ="uid" column ="uid" > </result > <result property ="money" column ="money" > </result > <association property ="user" javaType ="user" column ="uid" select ="com.uestc.dao.IUserDao.findById" > </association > </resultMap > <select id ="findAll" resultMap ="accountmap" > select * from account </select > </mapper >
3. 用户的持久层接口和映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { User findById (Integer userId) ; }
1 2 3 4 5 6 7 8 9 <?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.uestc.dao.IUserDao" > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id= #{id} </select > </mapper >
4. 开启Mybatis的延迟加载策略
我们需要在Mybatis的配置文件SqlMapConfig.xml文件中添加延迟加载的配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?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 ="pro.properties" > </properties > <settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings > <typeAliases > <package name ="com.uestc.domain" > </package > </typeAliases > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="jdbc" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" > </property > <property name ="url" value ="${jdbc.url}" > </property > <property name ="username" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </dataSource > </environment > </environments > <mappers > <package name ="com.uestc.dao" > </package > </mappers > </configuration >
5. 编写测试只查账户信息不查用户信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.uestc.test;import com.uestc.dao.IAccountDao;import com.uestc.dao.IUserDao;import com.uestc.domain.Account;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class AccountTest { private InputStream in; private SqlSession sqlSession; private IAccountDao accountDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); accountDao=sqlSession.getMapper(IAccountDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () { List<Account> accounts =accountDao.findAll(); } }
未开启延迟加载
开启了延迟加载
使用Collection实现延迟加载 同样我们也可以在一对多关系配置的<collection>
结点中配置延迟加载策略。 <collection>
结点中也有select
属性,column
属性。
需求: 完成加载用户对象时,查询该用户所拥有的账户信息
1. 在User实体类中加入List<Account>
属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.uestc.domain;import java.io.Serializable;import java.util.Date;import java.util.List;public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; private List<Account> accounts; public List<Account> getAccounts () { return accounts; } public void setAccounts (List<Account> accounts) { this .accounts = accounts; } public User () {}; public User (Integer id, String username, String address, String sex, Date birthday) { this .id = id; this .username = username; this .address = address; this .sex = sex; this .birthday = birthday; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}' ; } }
2. 编写用户和账户持久层接口的方法 用户持久层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { List<User> findAll () ; }
账户持久层接口
1 2 3 4 5 6 7 8 9 10 11 package com.uestc.dao;import com.uestc.domain.Account;import java.util.List;public interface IAccountDao { List<Account> findByUid (int uid) ; }
3. 编写用户持久层映射配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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.uestc.dao.IUserDao" > <resultMap id ="usermap" type ="user" > <id property ="id" column ="id" > </id > <result property ="username" column ="username" > </result > <result property ="address" column ="address" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > <collection property ="accounts" ofType ="account" select ="com.uestc.dao.IAccountDao.findByUid" column ="id" > </collection > </resultMap > <select id ="findAll" resultMap ="usermap" > select * from user </select > </mapper >
<collection>
标签: 主要用于加载关联的集合对象
4. 编写账户持久层映射配置 1 2 3 4 5 6 7 8 9 10 <?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.uestc.dao.IAccountDao" > <select id ="findByUid" parameterType ="int" resultType ="account" > select * from account where uid =#{uid} </select > </mapper >
5. 测试只加载用户信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindAll () { List<User> users=userDao.findAll(); } }
未开启延迟加载
开启延迟加载
Mybatis缓存 什么是缓存?
存在于内存中的临时数据
为什么使用缓存?
减少和数据库的交互次数,提高执行效率
什么样的数据适用于缓存?
检查查询并且不经常改变的
数据的正确与否对最终结果影响不大的
而经常改变的数据,且数据的正确与否对最终结果影响很大的,例如:商品的库存、银行的汇率和股市的牌价
Mybatis一级缓存
一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlSession中查询是否有,有的话就直接拿出来用
当SqlSession对象消失是,mybatis的一级缓存也就消失了
证明一级缓存的存在 1. 编写用户持久层Dao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { User findById (Integer userId) ; }
2. 编写用户持久层映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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.uestc.dao.IUserDao" > <select id ="findAll" resultType ="user" > select * from user </select > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > </mapper >
3. 编写测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory factory=builder.build(in); sqlSession=factory.openSession(); System.out.println(sqlSession); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindCache () { User user1=userDao.findById(41 ); System.out.println(user1); User user2=userDao.findById(41 ); System.out.println(user2); System.out.println(user1==user2); } }
我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
一级缓存的分析 一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
测试一级缓存的清空 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; private SqlSessionFactory factory; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestFindCache () { User user1=userDao.findById(41 ); System.out.println(user1); sqlSession.clearCache(); User user2=userDao.findById(41 ); System.out.println(user2); System.out.println(user1==user2); } }
当执行sqlSession.close()
后,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql 语句,从数据库进行了查询操作。
测试缓存同步 1. 编写dao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.uestc.dao;import com.uestc.domain.User;import java.util.List;public interface IUserDao { User findById (Integer userId) ; void updateUser (User user) ; }
2. 表写dao接口映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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.uestc.dao.IUserDao" > <select id ="findAll" resultType ="user" > select * from user </select > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > <select id ="updateUser" parameterType ="User" > update user set username=#{username}, address=#{address} where id=#{id} </select > </mapper >
3. 编写测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class UserTest { private InputStream in; private SqlSession sqlSession; private IUserDao userDao; private SqlSessionFactory factory; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); factory=builder.build(in); sqlSession=factory.openSession(); userDao=sqlSession.getMapper(IUserDao.class ) ; } @After public void destory () throws Exception { sqlSession.close(); in.close(); } @Test public void TestClearCache () { User user1=userDao.findById(41 ); System.out.println(user1); user1.setUsername("update clear cache" ); user1.setAddress("北京海淀区" ); userDao.updateUser(user1); sqlSession.commit(); User user2=userDao.findById(41 ); System.out.println(user2); System.out.println(user1==user2); } }
Mybatis二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存,由用一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存结构图
首先开启mybatis的二级缓存。
sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果SqlSession3去执行相同 mapper映射下sql,执行commit提交,将会清空该 mapper映射下的二级缓存区域的数据。
sqlSession2去查询与sqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据
二级缓存的开启与关闭 步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)
1. 第一步:在SqlMapConfig.xml文件开启二级缓存 1 2 3 4 <settings > <setting name ="cacheEnabled" value ="true" /> </settings >
因为cacheEnabled
的取值默认就为true
,所以这一步可以省略不配置。为true
代表开启二级缓存;为false代
表不开启二级缓存。
2. 第二步:配置相关的Mapper映射文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?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.uestc.dao.IUserDao" > <cache > </cache > <select id ="findAll" resultType ="user" > select * from user </select > <select id ="findById" parameterType ="int" resultType ="user" useCache ="true" > select * from user where id=#{id} </select > <select id ="updateUser" parameterType ="User" > update user set username=#{username}, address=#{address} where id=#{id} </select > </mapper >
3. 第三步:配置statement上面的useCache属性 1 2 3 4 <select id ="findById" parameterType ="int" resultType ="user" useCache ="true" > select * from user where id=#{id} </select >
UserDao.xml映射文件中的<select>
标签中设置useCache=”true”
代表当前这个statement要使用二级缓存,如果不使用二级缓存可以设置为false。 注意:针对每次查询都需要最新的数据sql,要设置成useCache=false
,禁用二级缓存。
4. 二级缓存测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.uestc.test;import com.uestc.dao.IUserDao;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;public class SecondLevelCached { private InputStream in; private SqlSessionFactory factory; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); factory=builder.build(in); } @After public void destory () throws Exception { in.close(); } @Test public void TestClearCache () { SqlSession sqlSession1= factory.openSession(); IUserDao userDao1=sqlSession1.getMapper(IUserDao.class ) ; User user1=userDao1.findById(41 ); System.out.println(user1); sqlSession1.close(); SqlSession sqlSession2= factory.openSession(); IUserDao userDao2=sqlSession2.getMapper(IUserDao.class ) ; User user2=userDao2.findById(41 ); System.out.println(user2); sqlSession1.close(); System.out.println(user1==user2); } }
经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出sql语句,所以此时的数据就只能是来自于我们所说的二级缓存。
为啥是false呢?因为二级缓存存入的不是对象而是数据
Mybatis注解开发
环境搭建
单标CRUD操作(代理Dao方法)
多表查询操作
缓存的配置
mybatis的常用注解说明
@Insert:
实现新增
@Update:
实现更新
@Delete:
实现删除
@Select:
实现查询
@Result
:实现结果集封装
@Results
:可以与@Result一起使用,封装多个结果集
@ResultMap
:实现引用@Results定义的封装
@One
:实现一对一结果集封装
@Many
:实现一对多结果集封装
@SelectProvider
: 实现动态SQL映射
@CacheNamespace
:实现注解二级缓存的使用
@Results注解 代替的是标签<resultMap>
该注解中可以使用单个@Result
注解,也可以使用@Result
集合 @Results({@Result(),@Result()})
或@Results(@Result())
@Resutl注解 代替了 <id>
标签和<result>
标签
@Result
中 属性介绍:
@One注解(一对一) 代替了<assocation>
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One
注解属性介绍:
使用格式: @Result(column=" ",property="",one=@One(select=""))
@Many注解(多对一) 代替了<Collection>
标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;
使用格式: @Result(property="",column="",many=@Many(select=""))
使用Mybatis注解实现基本CRUD 1. 编写实体类 注意: 此处我们故意和数据库表的列名不一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer userId; private String userName; private String userAddress; private String userSex; private Date UserBirthday; public Integer getUserId () { return userId; } public void setUserId (Integer userId) { this .userId = userId; } public String getUserName () { return userName; } public void setUserName (String userName) { this .userName = userName; } public String getUserAddress () { return userAddress; } public void setUserAddress (String userAddress) { this .userAddress = userAddress; } public String getUserSex () { return userSex; } public void setUserSex (String userSex) { this .userSex = userSex; } public Date getUserBirthday () { return UserBirthday; } public void setUserBirthday (Date userBirthday) { UserBirthday = userBirthday; } @Override public String toString () { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + ", userAddress='" + userAddress + '\'' + ", userSex='" + userSex + '\'' + ", UserBirthday=" + UserBirthday + '}' ; } }
2. 使用注解方式开发持久层接口 通过注解方式,我们就不需要再去编写UserDao.xml
映射文件了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package com.uestc.dao;import com.uestc.domain.User;import org.apache.ibatis.annotations.*;import org.junit.Test;import java.util.List;public interface IUser { @Select ("select * from user" ) @Results (id="usermap" , value ={ @Result (id =true , property ="userId" ,column="id" ), @Result ( property ="userName" ,column="username" ), @Result ( property ="userAddress" ,column="address" ), @Result ( property ="userSex" ,column="sex" ), @Result ( property ="userBirthday" ,column="birthday" ) } ) List<User> findAll () ; @Select ("select * from user where id=#{id}" ) @ResultMap ("usermap" ) User findById (Integer uid) ; @Insert ("insert into user(username, address,sex, birthday) values(#{userName},#{userAddress}, #{userSex},#{userBirthday})" ) void saveUser (User usr) ; @Update ("update user set username=#{userName}, address=#{userAddress} where id=#{userId}" ) void updateUser (User user) ; @Delete ("delete from user where id=#{uid}" ) void deleteUser (Integer uid) ; @Select ("select count(id) from user" ) int findTotol () ; @Select ("select * from user where username like #{name} " ) @ResultMap ("usermap" ) List<User> findByName (String name) ; }
3. 编写SqlMapConfig 配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?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 ="pro.properties" > </properties > <typeAliases > <package name ="com.uestc.domain" > </package > </typeAliases > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" > </property > <property name ="url" value ="${jdbc.url}" > </property > <property name ="username" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </dataSource > </environment > </environments > <mappers > <package name ="com.uestc.dao" > </package > </mappers > </configuration >
4. 编写测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 package com.uestc.test;import com.uestc.dao.IUser;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.Date;import java.util.List;public class TestAnnotation { private InputStream in; private SqlSessionFactory factory; private SqlSession sqlSession; private IUser userdao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); factory= new SqlSessionFactoryBuilder().build(in); sqlSession=factory.openSession(); userdao=sqlSession.getMapper(IUser.class ) ; } @After public void destory () throws Exception { sqlSession.commit(); sqlSession.close(); in.close(); } @Test public void testFindAll () { List<User> users=userdao.findAll(); for (User user:users){ System.out.println(user); } } @Test public void testFindById () { User user=userdao.findById(41 ); System.out.println(user); } @Test public void testSave () { User user=new User(); user.setUserName("快手" ); user.setUserSex("男" ); user.setUserAddress("北京快手总部" ); user.setUserBirthday(new Date()); userdao.saveUser(user); } @Test public void testUpdateUser () { User user=userdao.findById(42 ); user.setUserName("超女" ); user.setUserSex("女" ); userdao.updateUser(user); } @Test public void testDeleteUser () { userdao.deleteUser(41 ); } @Test public void TestFindTotol () { System.out.println(userdao.findTotol()); } @Test public void TestFindByName () { List<User> users=userdao.findByName("%王%" ); for (User user:users){ System.out.println(user); } } }
使用注解实现复杂关系映射开发 使用注解实现一对一复杂关系映射及延迟加载 需求: 加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载。(注解方式实现)
1. 添加User实体类及Account实体类 User实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.uestc.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable { private Integer userId; private String userName; private String userAddress; private String userSex; private Date UserBirthday; public Integer getUserId () { return userId; } public void setUserId (Integer userId) { this .userId = userId; } public String getUserName () { return userName; } public void setUserName (String userName) { this .userName = userName; } public String getUserAddress () { return userAddress; } public void setUserAddress (String userAddress) { this .userAddress = userAddress; } public String getUserSex () { return userSex; } public void setUserSex (String userSex) { this .userSex = userSex; } public Date getUserBirthday () { return UserBirthday; } public void setUserBirthday (Date userBirthday) { UserBirthday = userBirthday; } @Override public String toString () { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + ", userAddress='" + userAddress + '\'' + ", userSex='" + userSex + '\'' + ", UserBirthday=" + UserBirthday + '}' ; } }
Account实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.uestc.domain;public class Account { private Integer id; private Integer uid; private Double money; private User user; public User getUser () { return user; } public void setUser (User user) { this .user = user; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public Integer getUid () { return uid; } public void setUid (Integer uid) { this .uid = uid; } public Double getMoney () { return money; } public void setMoney (Double money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + '}' ; } }
2. 添加账户的持久层接口并使用注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.uestc.dao;import com.uestc.domain.Account;import org.apache.ibatis.annotations.One;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.mapping.FetchType;import java.util.List;public interface IAccount { @Select ({"select * from account" }) @Results (id = "accountmap" , value= { @Result (id = true , property = "id" , column = "id" ), @Result (property = "uid" , column = "uid" ), @Result (property = "money" , column = "money" ), @Result (property = "user" , column = "uid" , one=@One (select = "com.uestc.dao.IUser.findById" , fetchType= FetchType.EAGER) ) } ) List<Account> findAll () ; }
3. 添加用户的持久层接口并使用注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.uestc.dao;import com.uestc.domain.User;import org.apache.ibatis.annotations.*;import org.junit.Test;import java.util.List;public interface IUser { @Select ("select * from user" ) @Results (id="usermap" , value ={ @Result (id =true , property ="userId" ,column="id" ), @Result ( property ="userName" ,column="username" ), @Result ( property ="userAddress" ,column="address" ), @Result ( property ="userSex" ,column="sex" ), @Result ( property ="userBirthday" ,column="birthday" ) } ) List<User> findAll () ; @Select ("select * from user where id=#{id}" ) @ResultMap ("usermap" ) User findById (Integer uid) ; }
4. 测试一对一关联及延迟加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.uestc.test;import com.uestc.dao.IAccount;import com.uestc.dao.IUser;import com.uestc.domain.Account;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.List;public class TestAccount { private InputStream in; private SqlSessionFactory factory; private SqlSession sqlSession; private IAccount accountdao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); factory= new SqlSessionFactoryBuilder().build(in); sqlSession=factory.openSession(); accountdao=sqlSession.getMapper(IAccount.class ) ; } @After public void destory () throws Exception { sqlSession.commit(); sqlSession.close(); in.close(); } @Test public void testFindAll () { List<Account> accounts=accountdao.findAll(); } }
使用注解实现一对多复杂关系映射 需求: 查询用户信息时,也要查询他的账户列表。使用注解方式实现。
分析: 一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
1. User实体类加入List<Account>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.uestc.domain;import java.io.Serializable;import java.util.Date;import java.util.List;public class User implements Serializable { private Integer userId; private String userName; private String userAddress; private String userSex; private Date UserBirthday; private List<Account> accounts; public List<Account> getAccounts () { return accounts; } public void setAccounts (List<Account> accounts) { this .accounts = accounts; } public Integer getUserId () { return userId; } public void setUserId (Integer userId) { this .userId = userId; } public String getUserName () { return userName; } public void setUserName (String userName) { this .userName = userName; } public String getUserAddress () { return userAddress; } public void setUserAddress (String userAddress) { this .userAddress = userAddress; } public String getUserSex () { return userSex; } public void setUserSex (String userSex) { this .userSex = userSex; } public Date getUserBirthday () { return UserBirthday; } public void setUserBirthday (Date userBirthday) { UserBirthday = userBirthday; } @Override public String toString () { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + ", userAddress='" + userAddress + '\'' + ", userSex='" + userSex + '\'' + ", UserBirthday=" + UserBirthday + '}' ; } }
2. 编写用户的持久层接口并使用注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.uestc.dao;import com.uestc.domain.User;import org.apache.ibatis.annotations.*;import org.apache.ibatis.mapping.FetchType;import org.junit.Test;import java.util.List;public interface IUser { @Select ("select * from user" ) @Results (id="usermap" , value ={ @Result (id =true , property ="userId" ,column="id" ), @Result ( property ="userName" ,column="username" ), @Result ( property ="userAddress" ,column="address" ), @Result ( property ="userSex" ,column="sex" ), @Result ( property ="userBirthday" ,column="birthday" ), @Result (property = "accounts" , column = "id" , many = @Many ( select = "com.uestc.dao.IAccount.findById" , fetchType = FetchType.LAZY) ) } ) List<User> findAll () ; }
@Many
: 相当于<collection>
的配置
select属性
:代表将要执行的sql语句
fetchType
属性:代表加载方式,一般如果要延迟加载都设置为LAZY
的值
3. 编写账户的持久层接口并使用注解配置 1 2 3 4 5 6 7 @Select ("select * from account where uid=#{id}" ) List<Account> findById (int uid) ; }
4. 添加测试方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.uestc.test;import com.uestc.dao.IUser;import com.uestc.domain.User;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.After;import org.junit.Before;import org.junit.Test;import java.io.InputStream;import java.util.Date;import java.util.List;public class TestAnnotation { private InputStream in; private SqlSessionFactory factory; private SqlSession sqlSession; private IUser userdao; @Before public void init () throws Exception { in= Resources.getResourceAsStream("SqlMapConfig.xml" ); factory= new SqlSessionFactoryBuilder().build(in); sqlSession=factory.openSession(); userdao=sqlSession.getMapper(IUser.class ) ; } @After public void destory () throws Exception { sqlSession.commit(); sqlSession.close(); in.close(); } @Test public void testFindAll () { List<User> users=userdao.findAll(); for (User user:users){ System.out.println("-----每个用户下的账户信息------" ); System.out.println(user); System.out.println(user.getAccounts()); } } }
mybatis基于注解的二级缓存 1. 在SqlMapConfig中开启二级缓存支持 1 2 3 4 5 <settings > <setting name ="cacheEnabled" value ="true" /> </settings >
2. 在持久层接口中使用注解配置二级缓存 1 2 @CacheNamespace (blocking=true )public interface IUserDao {}