第一天
第一天基础知识:
1、对原生态JDBC程序(单独使用jdbc开发)问题总结
2、mybatis框架原理
3、mybatis入门程序:用于的增、删、改、查
4、mybatis开发dao两种方法
原始dao开发方法(程序需要编写dao接口和dao实现类)
mybatis的mapper接口(相当于dao接口)代理开发方法
5、mybatis配置文件SqlMapConfig.xml
6、 mybatis输入映射
mybatis输出映射
7、mybatis的动态sql
1、对原生态JDBC程序(单独使用jdbc开发)问题总结
1.1、使用原生态的JDBC操作数据库
-
数据库表:
/* SQLyog v10.2 MySQL - 5.1.72-community : Database - mybatis ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*Table structure for table `items` */ CREATE TABLE `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL COMMENT '商品名称', `price` float(10,1) NOT NULL COMMENT '商品定价', `detail` text COMMENT '商品描述', `pic` varchar(64) DEFAULT NULL COMMENT '商品图片', `createtime` datetime NOT NULL COMMENT '生产日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; /*Table structure for table `orderdetail` */ CREATE TABLE `orderdetail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `orders_id` int(11) NOT NULL COMMENT '订单id', `items_id` int(11) NOT NULL COMMENT '商品id', `items_num` int(11) DEFAULT NULL COMMENT '商品购买数量', PRIMARY KEY (`id`), KEY `FK_orderdetail_1` (`orders_id`), KEY `FK_orderdetail_2` (`items_id`), CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; /*Table structure for table `orders` */ CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '下单用户id', `number` varchar(32) NOT NULL COMMENT '订单号', `createtime` datetime NOT NULL COMMENT '创建订单时间', `note` varchar(100) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`), KEY `FK_orders_1` (`user_id`), CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; /*Table structure for table `user` */ CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性别', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /* SQLyog v10.2 MySQL - 5.1.72-community : Database - mybatis ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*Data for the table `items` */ insert into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'台式机',3000.0,'该电脑质量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'背包',200.0,'名牌背包,容量大质量好!!!!',NULL,'2015-02-06 13:23:02'); /*Data for the table `orderdetail` */ insert into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3); /*Data for the table `orders` */ insert into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL); /*Data for the table `user` */ insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'张三','2014-07-10','1','北京市'),(16,'张小明',NULL,'1','河南郑州'),(22,'陈小明',NULL,'1','河南郑州'),(24,'张三丰',NULL,'1','河南郑州'),(25,'陈小明',NULL,'1','河南郑州'),(26,'王五',NULL,NULL,NULL); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-
导连接数据的jar包
-
通过原生态的jdbc编程连接数据库
package com.zwd.mybatis.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 原生态JDBC编程中的问题 * @author Administrator * */ public class JdbcTest { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis","root","root"); //3.获取预处理对象 String sql = "select * from user where username = ?"; PreparedStatement ps = conn.prepareStatement(sql); //4.操作数据库并获取结果集 ps.setString(1, "陈小明"); ResultSet rs = ps.executeQuery(); //5.处理结果集 while(rs.next()){ System.out.println(rs.getString("id") + " " + rs.getString("username")); } //6.关闭资源 rs.close(); ps.close(); conn.close(); } }
1.2、问题总结
1、数据库连接,使用时就创建,不适用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库的性能。
2、将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3、向preparedStatement中设置参数,对占位符号的位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句以及占位符号和参数全部配置在xml中。
4、从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。
2、Mybatis
2.1、什么是mybatis
1、mybatis是一个持久层框架,是apache下的顶级项目。mybatis托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)
2、mybatis让程序员将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要的sql语句
3、mybatis可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
2.2、mybatis框架
2.3、mybatis环境配置
-
mybatis的jar下载地址
https://github.com/mybatis/mybatis-3/releases
-
导包:核心包 + lib下面的包 + 数据库驱动
-
在classpath下创建
log4j.properties
文件:用来记录程序运行的日志# Global logging configuration # 在开发环境下使用DEBUG;在生产环境下使用info或者error log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
-
在classpath下创建
SqlMapConfig.xml
文件 -
创建sqlMap包,用于之后存放
mapper.xml
文件
3、mybatis入门程序
3.1、查询单条记录-根据id查询用户(user)
-
配置
SqlMapConfig.xml
:<?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> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> </configuration>
-
创建po(persistent object 持久对象)类:
User.java
package com.zwd.mybatis.po; import java.util.Date; /******************************************************************************* * javaBeans * user --> User * <table explanation> * @author 2019-09-09 11:14:42 * */ public class User implements java.io.Serializable { //field /** **/ private int id; /** 用户名称 **/ private String username; /** 生日 **/ private Date birthday; /** 性别 **/ private String sex; /** 地址 **/ private String address; //method public int getId() { return id; } public void setId(int 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 birthday) { this.birthday = birthday; } 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 toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'id':'"+this.getId()+"',"); sb.append("'username':'"+this.getUsername()+"',"); sb.append("'birthday':'"+this.getBirthday()+"',"); sb.append("'sex':'"+this.getSex()+"',"); sb.append("'address':'"+this.getAddress()+"'"); sb.append("}"); return sb.toString(); } }
-
在sqlMap包下创建映射文件:
User.xml
映射文件命名: User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml
<?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"> <!-- namespace命名空间:作用就是对sql进行分类化管理,理解sql隔离 注意:使用mapper代理开发,namespace有特殊重要的作用。 --> <mapper namespace="test"> <!-- 在映射文件配置很多sql语句 --> <!-- 通过id查询用户的信息: select标签: id:标识映射文件中的sql parameterType:指定输入参数的类型 resultType:指定输出结果将会映射的对象类型。 select指定的resultType表示将单条记录映射成的java对象类型 sql查询语句: #{}:表示一个占位符 #{id}:其中的id表示接受的输入参数,参数名称为id。 如果输入参数为简单类型,参数名称可以任意。可以是value或其他值--> <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User"> select * from user where id = #{id} </select> </mapper>
-
在
SqlMapConfig.xml
中加载映射文件<!-- 加载映射文件 --> <mappers> <mapper resource="sqlMap/User.xml"/> </mappers>
<?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> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 加载映射文件 --> <mappers> <mapper resource="sqlMap/User.xml"/> </mappers> </configuration>
-
编写程序:
package com.zwd.mybatis.test; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import com.zwd.mybatis.po.User; /** * * @author Administrator * */ public class MybatisDemo1 { /** * 根据id查询用户: * <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User"> select * from user where id = #{id} * </select> * @throws IOException */ @Test public void findUserById() throws IOException { //1、获取会话工厂 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2、根据会话工厂获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //3、用SqlSession操作数据库 //selectOne(statement, parameter): //第一个参数:映射文件中statement的id,值为:namespace+"."+statement的id //第二个参数:指定和映射文件所匹配的parameterType的resultType类型的对象 User user = sqlSession.selectOne("test.findUserById", 1); System.out.println(user); //4、关闭资源 sqlSession.close(); } }
3.2、 查询多条记录-根据用户名称模糊查询用户记录
User.xml
<!-- 根据用户名称模糊查询用户列表:可能会返回多条记录
resultType:指定的就是单条记录所映射的java对象类型
${}:表示拼接sql串,将接收到的参数内容不加任何修饰拼接在sql中。
使用${}拼接sql,会引起sql注入问题
${value}:接收输入参数的内容,如果是简单类型,${}中只能使用value
-->
<select id="findUserByName" parameterType="java.lang.String" resultType="com.zwd.mybatis.po.User">
select * from user where username like '%${value}%'
</select>
/**
* 根据用户名称模糊查询用户列表
* @throws IOException
*/
@Test
public void findUserByName() throws IOException{
//1、获取会话工厂
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、根据会话工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、使用sqlSession操作数据库
// //select * from user where username like '%小明%'
// List<User> users = sqlSession.selectList("test.findUserByName");
// for (User user : users) {
// System.out.println(user);
// }
//select * from user where username like '%${value}%'
List<User> users = sqlSession.selectList("test.findUserByName", "小明");
for (User user : users) {
System.out.println(user);
}
}
3.3、添加用户
- 映射文件:
<!-- 插入用户:
parameterType:输入参数类型是pojo:User
#{}:中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值-->
<select id="insertUser" parameterType="com.zwd.mybatis.po.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</select>
-
编写程序:
/** * 插入用户 * @throws IOException */ @Test public void insertUser() throws IOException{ //1.获取会话工厂 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2.根据会话工厂获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //3.使用SqlSession操作数据库 User user = new User(); user.setUsername("赵文迪"); user.setAddress("贵州遵义"); user.setBirthday(new Date()); user.setSex("女"); sqlSession.insert("test.insertUser", user); sqlSession.commit(); sqlSession.close(); }
添加用户-自增主键返回
映射文件
<!--将插入数据的主键返回:
select LAST_INSERT_ID():得到刚insert进去的记录的主键值,只适用与自增主键。
keyProperty:将查询到的主键值设置到parameterType指定的对象的哪个属性中。
order:指定select LAST_INSERT_ID()语句相对于insert语句的执行顺序
resultType:指定select LAST_INSERT_ID()的结果类型
-->
<insert id="insertUser1" parameterType="com.zwd.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into
user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
程序代码:
/**
* 插入用户
* @throws IOException
*/
@Test
public void insertUser1() throws IOException{
//1.获取会话工厂
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.根据会话工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.使用SqlSession操作数据库
User user = new User();
user.setUsername("赵文迪");
user.setAddress("贵州遵义");
user.setBirthday(new Date());
user.setSex("女");
sqlSession.insert("test.insertUser1", user);
System.out.println(user.getId());
sqlSession.commit();
sqlSession.close();
}
添加用户-非自增主键返回
使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。
执行思路:
先通过uuid()查询到主键,将主键输入 到sql语句中。
执行uuid()语句顺序相对于insert语句之前执行。
删除用户和更新用户
映射文件:
<!-- 4、删除用户 -->
<delete id="deleteUserById" parameterType="java.lang.Integer">
delete from user where
id = #{id}
</delete>
<!-- 5、更新用户: 注意:id必须存在 -->
<update id="updateUser" parameterType="com.zwd.mybatis.po.User">
update user set
username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id = #{id}
</update>
程序代码:
/**
* 删除用户
* @throws IOException
*/
@Test
public void deleteUserById() throws IOException{
//1、获取会话工厂
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、根据会话工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、通过sqlSession操作数据库
sqlSession.delete("test.deleteUserById", 26);
//4、提交事务
sqlSession.commit();
//5、关闭会话
sqlSession.close();
}
/**
* 更新用户
* @throws IOException
*/
@Test
public void updateUser() throws IOException{
//1、获取会话工厂
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、根据会话工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、通过sqlSession操作数据库
User user = new User();
user.setId(25);
user.setUsername("赵文迪");
user.setAddress("贵州遵义");
user.setBirthday(new Date());
user.setSex("女");
sqlSession.update("test.updateUser", user);
//4、提交事务
sqlSession.commit();
//5、关闭会话
sqlSession.close();
}
3.4、总结
parameterType
在映射文件中通过parameterType指定输入 参数的类型。
ResultType
在映射文件中通过resultType指定输出结果的类型。
#{}和${}
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,#{}中可以写成value或其它名称。
#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
${}表示一个拼接符号,会引用sql注入,所以不建议使用${}。
${}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,${}中只能写成value。
${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
selectOne和selectList
selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。
selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。
3.5 mybatis和hibernate本质区别和应用场景
hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。
对sql语句进行优化、修改比较困难的。
应用场景:
适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。
mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全 的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。
应用场景:
适用与需求变化较多的项目,比如:互联网项目。
企业进行技术选型,以低成本 高回报作为技术选型的原则,根据项目组的技术力量进行选择。
4、mybatis开发dao的方法
4.1、SqlSession的使用范围
SqlSessionFactoryBuilder
通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。
在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。
SqlSessionFactory
通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。
将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory。
SqlSession
SqlSession是一个面向用户(程序员)的接口。
SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。
SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。
SqlSession最佳应用场合在方法体内,定义成局部变量使用。
4.2、原始dao的开发方法(程序员需要写dao接口和dao实现方法)
-
使用上述的
SqlMapConfig.xml
和user.xml
的配置文件。 -
编写dao接口:
UserDao.java
package com.zwd.mybatis.dao; import com.zwd.mybatis.po.User; /** * 用户持久层接口 * @author Administrator * */ public interface UserDao { /** * 根据id查询用户 * @param id * @return */ public User findUserById(Integer id); /** * 插入用户 * @param user */ public void insertUser(User user); }
-
编写dao的实现类:
UserDaoImpl.java
package com.zwd.mybatis.dao.impl; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import com.zwd.mybatis.dao.UserDao; import com.zwd.mybatis.po.User; public class UserDaoImpl implements UserDao { private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById(Integer id) { SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("test.findUserById", id); return user; } @Override public void insertUser(User user) { SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("test.insertUser", user); } }
-
测试方法:
package com.zwd.mybatis.test; import static org.junit.Assert.fail; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.zwd.mybatis.dao.UserDao; import com.zwd.mybatis.dao.impl.UserDaoImpl; import com.zwd.mybatis.po.User; public class UserDaoImplTest { private SqlSessionFactory sqlSessionFactory; @Before public void setUp() throws IOException { InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() { UserDao dao = new UserDaoImpl(sqlSessionFactory); User user = dao.findUserById(1); System.out.println(user); } @Test public void testInsertUser() { fail("Not yet implemented"); } }
-
问题:
1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。 2、调用sqlsession方法时将statement的id硬编码了 3、调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
4.3、mapper代理方法
程序员需要写mapper接口,相当于dao接口;还需要编写mapper.xml
配置文件。
程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
开发规范:
1、在mapper.xml中namespace等于mapper接口的全类名
2、mapper.java接口中的方法名和mapper.xml中statement的id一致
3、mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
4、mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。
-
userMapper.xml
<?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"> <!-- namespace命名空间:作用就是对sql进行分类化管理,理解sql隔离 注意:使用mapper代理开发,namespace有特殊重要的作用,namespace等于mapper接口的全限定类名。 --> <mapper namespace="com.zwd.mybatis.mapper.UserMapper"> <!-- 1、通过id查询用户的信息--> <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User"> select * from user where id = #{id} </select> <!--2、 插入用户 --> <insert id="insertUser" parameterType="com.zwd.mybatis.po.User"> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
-
UserMapper.java
:mapper接口(dao接口)package com.zwd.mybatis.mapper; import com.zwd.mybatis.po.User; /** * 用户持久层接口 * @author Administrator * */ public interface UserMapper { /** * 根据id查询用户 * @param id * @return * <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User"> */ public User findUserById(Integer id); /** * 插入用户 * @param user * <insert id="insertUser" parameterType="com.zwd.mybatis.po.User"> */ public void insertUser(User user); }
-
在mybatis的总配置文件中加载映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 加载映射文件 --> <mappers> <mapper resource="mapper/userMapper.xml"/> </mappers> </configuration>
-
测试:
//创建mapper对象,mybatis自动创建mapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findUserById(1);
@Before public void setUp() throws IOException { InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findUserById(1); System.out.println(user); }
5、SqlMapConfig.xml配置文件
mybatis的全局配置文件SqlMapConfig.xml,配置内容如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
5.1、properties属性
properties特性:
注意: MyBatis 将按照下面的顺序来加载属性:
在 properties 元素体内定义的属性首先被读取。
然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
最后读取parameterType传递的属性,它会覆盖已读取的同名属性。
建议:
不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX
需求:
将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。
在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。
-
添加
db.properties
数据库连接信息文件jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.user=root jdbc.password=root
-
在
SqlMapConfig.xml
中的配置<!-- 加载数据库的配置文件 --> <properties resource="db.properties"> <!-- <property name="jdbc.Driver" value=""/> --> </properties> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </dataSource>
<?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="db.properties"> </properties> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <!-- 加载映射文件 --> <mappers> <mapper resource="mapper/userMapper.xml"/> </mappers> </configuration>
5.2、settings(全局配置参数)
mybatis框架在运行时可以调整一些运行参数。
比如:开启二级缓存、开启延迟加载。。
全局参数将会影响mybatis的运行行为。
5.3、typeAliases(类型别名)
- 需求:
在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。
如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
mybatis默认支持的别名:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
自定义别名(对于自己定义的类型需要自己定义别名)
- 配置:
<!-- 别名定义: -->
<typeAliases>
<!-- 单个别名定义:
type:类的全限定类名
alias:别名-->
<typeAlias type="com.zwd.mybatis.po.User" alias="user"/>
<!-- 批量定义:
指定包名,mybatis自动扫描包中的类,自定义类的别名,别名就是类名(首字母大写或小写都可以)-->
<package name="com.zwd.mybatis.po"/>
</typeAliases>
<?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="db.properties">
<!-- <property name="jdbc.Driver" value=""/> -->
</properties>
<!-- 别名定义: -->
<typeAliases>
<!-- 单个别名定义:
type:类的全限定类名
alias:别名-->
<typeAlias type="com.zwd.mybatis.po.User" alias="user"/>
<!-- 批量定义:
指定包名,mybatis自动扫描包中的类,自定义类的别名,别名就是类名(首字母大写或小写都可以)-->
<package name="com.zwd.mybatis.po"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
5.4、typeHandlers(类型处理器)
mybatis中通过typeHandlers完成jdbc类型和java类型的转换。
通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义.
mybatis支持类型处理器:
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | Boolean,boolean | 任何兼容的布尔值 |
ByteTypeHandler | Byte,byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | Short,short | 任何兼容的数字或短整型 |
IntegerTypeHandler | Integer,int | 任何兼容的数字和整型 |
LongTypeHandler | Long,long | 任何兼容的数字或长整型 |
FloatTypeHandler | Float,float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | Double,double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | String | CHAR和VARCHAR类型 |
ClobTypeHandler | String | CLOB和LONGVARCHAR类型 |
NStringTypeHandler | String | NVARCHAR和NCHAR类型 |
NClobTypeHandler | String | NCLOB类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB和LONGVARBINARY类型 |
DateTypeHandler | Date(java.util) | TIMESTAMP类型 |
DateOnlyTypeHandler | Date(java.util) | DATE类型 |
TimeOnlyTypeHandler | Date(java.util) | TIME类型 |
SqlTimestampTypeHandler | Timestamp(java.sql) | TIMESTAMP类型 |
SqlDateTypeHandler | Date(java.sql) | DATE类型 |
SqlTimeTypeHandler | Time(java.sql) | TIME类型 |
ObjectTypeHandler | 任意 | 其他或未指定类型 |
EnumTypeHandler | Enumeration类型 | VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。 |
5.5、mappers(映射配置)
加载单个的映射文件(mapper代理方式的dao)
-
方式一:
<!-- 方式一:通过的映射文件来加载单个映射文件 --> <mapper resource="mapper/userMapper.xml"/>
-
方式二:
<!-- 方式二:通过mapper接口来加载单个映射文件 需要遵循的规范:需要把mapper接口的类名和mapper.xml的映射文件名保持一致, 并且再一个目录中,上边规范的前提是:使用mapper代理方法。--> <mapper class="com.zwd.mybatis.mapper.UserMapper"/>
批量加载(mapper代理方式的dao)-推荐使用
<!-- 方式三:批量加载mapper映射文件
指定mapper接口的包名,mybatis自动扫描包下所有mapper接口进行加载。
需要遵循的规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,
且在一个目录中。
规范的前提:使用的是mapper代理方法。 -->
<package name="com.zwd.mybatis.mapper"/>
6、输入映射(parameterType)
通过parameterType指定输入参数的类型,类型可以使简单类型,hashMap,简单的pojo对象、pojo的包装类型
6.1、传递简单类型
<!-- 1、通过id查询用户的信息-->
<select id="findUserById" parameterType="int"
resultType="com.zwd.mybatis.po.User">
select * from user where id = #{id}
</select>
6.2、传递pojo对象
<!--2、 插入用户 -->
<insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
insert into
user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
6.3、传递pojo的包装类型
需求
完成客户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息,其他信息,比如商品、订单等。)
定义包装类型pojo
package com.zwd.mybatis.po;
/**
* 用户的包装类型
* @author Administrator
*
*/
public class UserQueryVo {
//这里包装所需要的查询条件
//用户查询条件
private UserCustomer user;
public UserCustomer getUser() {
return user;
}
public void setUser(UserCustomer user) {
this.user = user;
}
//可以包装其他的查询条件,订单、商品
}
package com.zwd.mybatis.po;
/**
* 用户的扩展类
* @author Administrator
*
*/
public class UserCustomer extends User{
//可以扩展用户的信息
}
mapper.xml
<!-- 3、用户的综合查询 -->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
</select>
<?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">
<!-- namespace命名空间:作用就是对sql进行分类化管理,理解sql隔离
注意:使用mapper代理开发,namespace有特殊重要的作用,namespace等于mapper接口的全限定类名。 -->
<mapper namespace="com.zwd.mybatis.mapper.UserMapper">
<!-- 1、通过id查询用户的信息-->
<select id="findUserById" parameterType="int"
resultType="com.zwd.mybatis.po.User">
select * from user where id = #{id}
</select>
<!--2、 插入用户 -->
<insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
insert into
user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 3、用户的综合查询 -->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
</select>
</mapper>
mapper.java
/**
* 综合查询用户
* <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
* </select>
*/
public List<UserCustomer> findUserList(UserQueryVo userVo);
package com.zwd.mybatis.mapper;
import java.util.List;
import com.zwd.mybatis.po.User;
import com.zwd.mybatis.po.UserCustomer;
import com.zwd.mybatis.po.UserQueryVo;
/**
* 用户持久层接口
* @author Administrator
*
*/
public interface UserMapper {
/**
* 根据id查询用户
* @param id
* @return
* <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
*/
public User findUserById(Integer id);
/**
* 插入用户
* @param user
* <insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
*/
public void insertUser(User user);
/**
* 综合查询用户
* <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
* </select>
*/
public List<UserCustomer> findUserList(UserQueryVo userVo);
}
测试
/**
* 综合查询
* @throws IOException
*/
@Test
public void findUserList() throws IOException{
// InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userVo = new UserQueryVo();
UserCustomer userCustomer = new UserCustomer();
userCustomer.setSex("女");
userCustomer.setUsername("赵文迪");
userVo.setUser(userCustomer);
//调用userMapper的方法
List<UserCustomer> userList = userMapper.findUserList(userVo);
for (UserCustomer user : userList) {
System.out.println(user);
}
}
6.4、传递HashMap(和传递简单的pojo对象一样)
通过键来取值
mapper.xml
<!-- 4、传递hashMap -->
<select id="findUserListByHashMap" parameterType="java.util.HashMap"
resultType="com.zwd.mybatis.po.User">
select * from user where sex=#{sex} and username like '${username}'
</select>
mapper.java
/**
* 输入参数为HashMap
* <!-- 4、传递hashMap -->
<select id="findUserListByHashMap" parameterType="java.util.HashMap"
resultType="com.zwd.mybatis.po.User">
select * from user where sex=#{sex} and username like '${username}'
</select>
* @return
*/
public List<User> findUserListByHashMap(HashMap map);
测试
@Test
public void findByHashMap(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap<>();
map.put("sex", "女");
map.put("username", "赵文迪");
List<User> list = userMapper.findUserListByHashMap(map);
for (User user : list) {
System.out.println(user);
}
}
7、输出映射
7.1、resultType
使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功
如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
如果查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。
输出简单类型
mapper.xml
<!-- 5、输出简单类型:用户信息的综合查询总数 -->
<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="int">
select count(*) from user where sex=#{user.sex} and username like'${user.username}'
</select>
mapper.java
/**
* <!-- 5、输出简单类型:用户信息的综合查询总数 -->
<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="int">
select count(*) from user where sex=#{user.sex} and username like'${user.username}'
</select>
* @param userVo
* @return
*/
public int findUserCount(UserQueryVo userVo);
测试
/**
* 输出类型为简单类型
*/
@Test
public void findUserCount(){
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userVo = new UserQueryVo();
UserCustomer userCustomer = new UserCustomer();
userCustomer.setSex("女");
userCustomer.setUsername("赵文迪");
userVo.setUser(userCustomer);
//调用userMapper的方法
int userCount = userMapper.findUserCount(userVo);
System.out.println(userCount);
}
输出pojo对象和pojo对象的列表
不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。在mapper.java指定的方法返回值类型不一样。
<!--返回单个pojo对象-->
<select id="findUserById" parameterType="int"
resultType="com.zwd.mybatis.po.User">
select * from user where id = #{id}
</select>
<!--返回一个pojo列表-->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
</select>
1、输出当个pojo对象,方法返回值是单个对象类型。
public User findUserById(Integer id);
2、输出pojo对象list,方法返回值是List
public List<UserCustomer> findUserList(UserQueryVo userVo);
生成的动态代理对象中是根据mapper方法的返回值类型确定确定是调用selectOne()
还是selectList()
输出HashMap
输出pojo对象可以改用hashmap输出类型,将输出的字段名称作为map的key,value为字段值。
7.2、resultMap
mybatis中使用resultMap完成高级输出结果映射
resultMap使用方法
如果查询出来的列名和pojo的属性名不一致,通过定义个resultMap对列名和pojo属性名之间作一个映射关系。
1、定义resultMap
2、使用resultMap作为statement的输出映射类型
对下列sql语句使用pojo类User作为输出映射
select id id_ , username username_ from user where id = #{id}
由于查询出来的列名和User中的属性名不一致,所以不能使用resultType
输出类型。需要使用resultMap
mapper.xml
<!-- 6、resultMap:通过id查询用户的信息-->
<!-- 6.1、定义resultMap
将select id id_ , username username_ from user和User类中的属性作一个映射关系。
type:resultType最终映射的java对象类型,可以使用别名。
id:对resultType的唯一标识 -->
<resultMap type="com.zwd.mybatis.po.User" id="userResultMap">
<!-- id表示查询结果集中的唯一标识:column:查询出来的列名;property:type指定的pojo类型中的属性名 -->
<id column="id_" property="id"/>
<!-- result表示查询结果集中的普通列名:column:查询出来的列名;property:type指定的pojo类型中的属性名 -->
<result column="username_" property="username"/>
</resultMap>
<!-- 6.2、使用resultMap作为输出类型 -->
<select id="findUserByIdResultMap" parameterType="int"
resultMap="userResultMap">
select id id_ , username username_ from user where id = #{id}
</select>
<!-- 6、resultMap:通过id查询用户的信息-->
<!-- 6.1、定义resultMap -->
<resultMap type="com.zwd.mybatis.po.User" id="userResultMap">
<id column="id_" property="id"/>
<result column="username_" property="username"/>
</resultMap>
<!-- 6.2、使用resultMap作为输出类型:
resultMap:指定定义的resultMap的id,如果这个resultMap在其他的mapper文件中,前边需要加namespace -->
<select id="findUserByIdResultMap" parameterType="int"
resultMap="userResultMap">
select id id_ , username username_ from user where id = #{id}
</select>
mapper.java
/**
* <!-- 6.2、使用resultMap作为输出类型 -->
<select id="findUserByIdResultMap" parameterType="int"
resultMap="userResultMap">
select id id_ , username username_ from user where id = #{id}
</select>
* @param id
* @return
*/
public User findUserByIdResultMap(int id);
测试
@Test
/**
* resultMap
*/
public void findUserByIdResultMap() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserByIdResultMap(25);
System.out.println(user);
}
8、动态sql
8.1、需求
用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。
对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。(如果条件给的值为null,拼接后执行报错)
8.2、mapper.xml
<!-- 3、用户的综合查询 -->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user
<where><!-- 自动去掉条件中的第一个and -->
<if test="user!=null">
<if test="user.sex!=null and user.sex!=''">
sex=#{user.sex}
</if>
<if test="user.username!=null and user.username!=''">
and username like'${user.username}'
</if>
</if>
</where>
</select>
<!-- 5、输出简单类型:用户信息的综合查询总数 -->
<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="int">
select count(*) from user
<where><!-- 自动去掉条件中的第一个and -->
<if test="user!=null">
<if test="user.sex!=null and user.sex!=''">
sex=#{user.sex}
</if>
<if test="user.username!=null and user.username!=''">
and username like'${user.username}'
</if>
</if>
</where>
</select>
8.3、mapper.java
/**
* 综合查询用户
* <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user where sex=#{user.sex} and username like'${user.username}'
* </select>
*/
public List<UserCustomer> findUserList(UserQueryVo userVo);
/**
* <!-- 5、输出简单类型:用户信息的综合查询总数 -->
<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="int">
select count(*) from user where sex=#{user.sex} and username like'${user.username}'
</select>
* @param userVo
* @return
*/
public int findUserCount(UserQueryVo userVo);
8.5、测试
在测试时可以使sql的条件参数值为null。
/**
* 综合查询
* @throws IOException
*/
@Test
public void findUserList() throws IOException{
// InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userVo = new UserQueryVo();
UserCustomer userCustomer = new UserCustomer();
userCustomer.setSex("女");
// userCustomer.setUsername("赵文迪");
userVo.setUser(userCustomer);
//调用userMapper的方法
List<UserCustomer> userList = userMapper.findUserList(userVo);
for (UserCustomer user : userList) {
System.out.println(user);
}
}
8.6、sql片段
需求
将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其他的statement中就可以引用该sql片段。
定义sql片段
<!-- 定义sql片段
经验:是基于单表来定义sql片段,这样的话sql片段重用性才高;
在sql片段汇总不要包括where -->
<sql id="query_user_where">
<if test="user!=null">
<if test="user.sex!=null and user.sex!=''">
sex=#{user.sex}
</if>
<if test="user.username!=null and user.username!=''">
and username like'${user.username}'
</if>
</if>
</sql>
引用sql片段
<!-- 3、用户的综合查询 -->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user
<where>
<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
<include refid="query_user_where"></include>
<!-- 还可以引用多个sql片段 -->
<!-- <include refid="query_user_where1"></include> -->
</where>
</select>
<!-- 5、输出简单类型:用户信息的综合查询总数 -->
<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="int">
select count(*) from user
<where>
<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
<include refid="query_user_where"></include>
</where>
</select>
8.7、foreach
需求
在用户查询列表和查询总数的statement中增加多个id输入查询。
sql语句如下:
(1)select * from user where id = 25 or id = 1 or id = 27
(2)select * from user where id in(1,25,27)
修改输入参数类型
在输入参数类型中添加一个list集合,用于存储多个id。UserQueryVo.java
package com.zwd.mybatis.po;
import java.util.List;
/**
* 用户的包装类型
* @author Administrator
*
*/
public class UserQueryVo {
//这里包装所需要的查询条件
//添加list集合,用于存储多个id
private List<Integer> ids;
//用户查询条件
private UserCustomer user;
public UserCustomer getUser() {
return user;
}
public void setUser(UserCustomer user) {
this.user = user;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
//可以包装其他的查询条件,订单、商品
}
mapper.xml
<!-- 定义sql片段
经验:是基于单表来定义sql片段,这样的话sql片段重用性才高;
在sql片段汇总不要包括where -->
<sql id="query_user_where">
<if test="user!=null">
<if test="user.sex!=null and user.sex!=''">
sex=#{user.sex}
</if>
<if test="user.username!=null and user.username!=''">
and username like'${user.username}'
</if>
<!-- select * from user where 1=1 and (id = 1 or id=25 or id = 27) -->
<if test="ids!=null">
<!-- 使用foreach遍历传入的ids
collection:指定输入对象中的集合属性名称
item:每个遍历生产的对象名称
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象中需要拼接的串
-->
<!-- 使用下列语句拼接:and (id = 1 or id=25 or id = 27) -->
<foreach collection="ids" item="user_id" open="and (" close=")" separator="or">
id=#{user_id}
</foreach>
<!--使用下列语句拼接:and id in(1,25,27) -->
<foreach collection="ids" item="user_id" open="and id in(" close=")" separator=",">
#{user_id}
</foreach>
</if>
</if>
</sql>
<!-- 3、用户的综合查询 -->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo"
resultType="com.zwd.mybatis.po.UserCustomer">
select * from user
<where>
<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
<include refid="query_user_where"></include>
<!-- 还可以引用多个sql片段 -->
<!-- <include refid="query_user_where1"></include> -->
</where>
</select>
测试
/**
* 综合查询
* @throws IOException
*/
@Test
public void findUserList() throws IOException{
// InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建包装对象,设置查询条件
UserQueryVo userVo = new UserQueryVo();
UserCustomer userCustomer = new UserCustomer();
userCustomer.setSex("女");
userCustomer.setUsername("赵文迪");
userVo.setUser(userCustomer);
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(25);
ids.add(27);
userVo.setIds(ids);
//调用userMapper的方法
List<UserCustomer> userList = userMapper.findUserList(userVo);
for (UserCustomer user : userList) {
System.out.println(user);
}
}
第二天
订单商品数据模型分析
高级结果集映射(一对一、一对多、多对多)
mybatis延迟加载
mybatis查询缓存(一级缓存、二级缓存)
mybatis和spring进行整合
mybatis的逆向工程
1、第一天复习
Mybatis框架执行过程:
1、配置mybatis的配置文件:SqlMapConfig.xml(名称不固定)
2、通过配置文件,加载mybatis运行环境,创建SqlSessionFactory会话工厂。
SqlSessionFactory在实际使用时按单例方式。
3、通过SqlSessionFactory创建SqlSession
SqlSession是一个面向用户的接口(提供操作数据库的方法),实例对象是线程不安全的,建议sqlSession应用场合在方法体内。
4、调用sqlSession的方法去操作数据库。
5、释放资源,关闭SqlSession。
mybatis开发dao的方法:
1、原始dao的方法(user.xml;userDao.java;userDaoImpl.java)
需要编写dao接口和dao实现类
需要在dao实现类中注入一个SqlSessionFactory会话工厂
2、mapper代理开发方法(建议使用;userMapper.xml;userMapper.java)
需要编写dao接口和mapper映射文件。
编写mapper.xml和mapper.java需要遵循一个开发规范:
(1)mapper.xml中的namespace就是mapper.java的类全路径名。
(2)mapper.xml中statement的id和mapper.java中方法名一致。
(3)mapper.xml中parameterType指定输入参数的类型和mapper.java的方法输入参数类型一致。
(4)mapper.xml中statement的resultType/resultMap指定输出结果的类型和mapper.java的方法 返回值类型一致。
输入映射:
parameterType:指定输入参数类型可以简单类型、pojo、hashMap。
对于综合查询,建议parameterType使用包装的pojo,有利于系统扩展。
输出映射:
resultType:
查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。
resultMap:
可以通过resultMap完成一些高级映射。
如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。
高级映射:
将关联查询的列映射到一个pojo属性中。(一对一)
将关联查询的列映射到一个List<pojo>中。(一对多)
**动态sql:**if判断、where、foreach、sql片段。
2、课程安排
1、对订单商品数据模型进行分析
2、高级映射:
实现一对一、一对多、多对多查询。
延迟加载。
3、查询缓存:
一级缓存
二级缓存
4、mybatis和spring整合。
5、逆向工程。
3、对订单商品数据模型进行分析
数据库表:
/*
Navicat MySQL Data Transfer
Source Server : ZWD
Source Server Version : 50711
Source Host : localhost:3306
Source Database : mybatis
Target Server Type : MYSQL
Target Server Version : 50711
File Encoding : 65001
Date: 2019-11-20 10:46:15
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `items`
-- ----------------------------
DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '商品名称',
`price` float(10,1) NOT NULL COMMENT '商品定价',
`detail` text COMMENT '商品描述',
`pic` varchar(64) DEFAULT NULL COMMENT '商品图片',
`createtime` datetime NOT NULL COMMENT '生产日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of items
-- ----------------------------
INSERT INTO `items` VALUES ('1', '台式机', '3000.0', '该电脑质量非常好!!!!', null, '2015-02-03 13:22:53');
INSERT INTO `items` VALUES ('2', '笔记本', '6000.0', '笔记本性能好,质量好!!!!!', null, '2015-02-09 13:22:57');
INSERT INTO `items` VALUES ('3', '背包', '200.0', '名牌背包,容量大质量好!!!!', null, '2015-02-06 13:23:02');
-- ----------------------------
-- Table structure for `orderdetail`
-- ----------------------------
DROP TABLE IF EXISTS `orderdetail`;
CREATE TABLE `orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orders_id` int(11) NOT NULL COMMENT '订单id',
`items_id` int(11) NOT NULL COMMENT '商品id',
`items_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`orders_id`),
KEY `FK_orderdetail_2` (`items_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orderdetail
-- ----------------------------
-- ----------------------------
-- Table structure for `orders`
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '10', '11', '2019-09-18 09:41:28', '33');
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
用户表user和订单表orders:
user–>orders:一个用户可以创建多个订单;一对多。
orders–>user:一个订单只由一个用户创建;一对一。
订单表orders和订单明细表orderdetail:
orders–>ordertail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail中记录;一对多。
ordertail–>orders:一个订单明细只能包括在一个订单中;一对一
订单明细orderdetail–>商品表items:
ordertail–>items:一个订单明细只对应一个商品信息;一对一。
items–>ordertail:一个商品可以包括在多个订单明细中;一对多。
4、一对一查询
4.1、需求
查询订单信息,关联查询创建订单的用户信息。
select orders.*,`user`.username,user.sex,`user`.address
FROM user,orders
where `user`.id = orders.user_id
4.2、方法一:resultType
添加一个pojo类:继承Orders类,添加用户的部分属性。
根据返回值的结果,po类Orders.java中的属性已经不满足结果字段,需要封装一个pojo类OrdersCustom.java,用于接收返回值的字段。
package com.zwd.mybatis.po;
/**
* 订单的扩展类
* @author Administrator
*
*/
//通过此类映射订单和用户查询的结果,让此类继承包括字段较多的pojo类
public class OrdersCustom extends Orders {
//添加用户的属性
private String username;
private String sex;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
mapper.xml
记住在SqlMapConfig.xml
中配置映射文件。
<?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.zwd.mybatis.mapper.OrdersMapper">
<select id="findOrders" parameterType="int" resultType="com.zwd.mybatis.po.OrdersCustom">
select orders.*,`user`.username,user.sex,`user`.address
from user,orders
where `user`.id = #{id}
</select>
</mapper>
mapper.java
package com.zwd.mybatis.mapper;
import java.util.List;
import com.zwd.mybatis.po.OrdersCustom;
public interface OrdersMapper {
public List<OrdersCustom> findOrders(int orderId);
}
测试
@Test
public void findOrders() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersCustom> orders = ordersMapper.findOrders(10);
System.out.println(orders);
}
对于上诉例子的问题:数据库中orders表中的字段:user_id与po类Order.java中的属性:userId名称不一致,所以映射的时候该字段不能映射成功,还是得用resultMap来重新对应字段和属性之间的关系。。
4.3、方法二:resultMap
不添加新的pojo类,将User的类对象添加到Orders类中,作为成员变量。
不添加pojo类,使用resultMap将查询结果中的订单信息映射到Orders对象中,需要在Orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。
Orders.java
package com.zwd.mybatis.po;
import java.sql.Timestamp;
import java.util.Date;
/*******************************************************************************
* javaBeans
* orders --> Orders
* <table explanation>
* @author 2019-11-20 11:38:05
*
*/
public class Orders implements java.io.Serializable {
//field
/** **/
private int id;
/** 下单用户id **/
private int userId;
/** 订单号 **/
private String number;
/** 创建订单时间 **/
private Timestamp createtime;
/** 备注 **/
private String note;
//添加User对象
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
//method
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Timestamp getCreatetime() {
return createtime;
}
public void setCreatetime(Timestamp createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
@Override
public String toString() {
return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + ", user=" + user + "]";
}
}
mapper.xml
<!-- 方式二: -->
<resultMap type="com.zwd.mybatis.po.Orders" id="ordersResultMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置映射关联的用户信息 -->
<!-- association:用于映射关联查询单个对象的信息
property:要将关联查询的用户信息映射到Orders中的哪个属性-->
<association property="user" javaType="com.zwd.mybatis.po.User">
<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="findOrders1" parameterType="int" resultMap="ordersResultMap">
select orders.*,`user`.username,user.sex,`user`.address
from user,orders
where `user`.id = #{id}
</select>
mapper.java
public List<Orders> findOrders1(int orderId);
测试
@Test
public void findOrders() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
//List<OrdersCustom> orders = ordersMapper.findOrders(10);
List<Orders> orders1 = ordersMapper.findOrders1(10);
System.out.println(orders1);
}
5、一对多
需求
查询某订单的订单明细信息。(包括用户信息)
select orders.*,`user`.username,user.sex,`user`.address,orderdetail.id orderdetail_id,orderdetail.items_num
FROM user,orders,orderdetail
where `user`.id = orders.user_id and orderdetail.orders_id= orders.id
在Orders.java
类中添加一Orderdetail
类的集合属性
//orders->orderdetail一对多关系映射:添加一个orderdetail集合
private List<Orderdetail> orderdetail;
package com.zwd.mybatis.po;
import java.sql.Timestamp;
import java.util.List;
/*******************************************************************************
* javaBeans
* orders --> Orders
* <table explanation>
* @author 2019-11-20 11:38:05
*
*/
public class Orders implements java.io.Serializable {
//field
/** **/
private int id;
/** 下单用户id **/
private int userId;
/** 订单号 **/
private String number;
/** 创建订单时间 **/
private Timestamp createtime;
/** 备注 **/
private String note;
//orders->user一对一关系映射:添加User对象
private User user;
//orders->orderdetail一对多关系映射:添加一个orderdetail集合
private List<Orderdetail> orderdetail;
public List<Orderdetail> getOrderdetail() {
return orderdetail;
}
public void setOrderdetail(List<Orderdetail> orderdetail) {
this.orderdetail = orderdetail;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
//method
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Timestamp getCreatetime() {
return createtime;
}
public void setCreatetime(Timestamp createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
@Override
public String toString() {
return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + ", user=" + user + "]";
}
}
mapper.xml
<!-- orders->orderdetail:一对多的关系映射 -->
<resultMap type="com.zwd.mybatis.po.Orders" id="ordersDetailsResultMap">
<!-- 订单信息 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 用户信息 -->
<association property="user" javaType="com.zwd.mybatis.po.User">
<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
<!-- 订单明细信息 -->
<!-- 一个订单关联查询出了多条订单明细,要使用collection进行映射。
collection:对关联查询到多条记录映射到集合对象中。
property:将关联查询到的多条记录映射到Orders中的哪个属性。
ofType:指定映射到哪个pojo类型的list集合-->
<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_num" property="itemsNum"/>
</collection>
</resultMap>
<select id="findOrdersUserDetails" resultMap="ordersDetailsResultMap">
select orders.*,`user`.username,user.sex,`user`.address,orderdetail.id orderdetail_id,orderdetail.items_num
FROM user,orders,orderdetail
where `user`.id = orders.user_id and orderdetail.orders_id= orders.id
</select>
改进:定义的resultMap
可以使用extends
继承,所以这里可以继承一对一查询中定义的resultMap。extends="ordersResultMap"
<!-- orders->orderdetail:一对多的关系映射 -->
<resultMap type="com.zwd.mybatis.po.Orders" id="ordersDetailsResultMap" extends="ordersResultMap">
<!-- 订单明细信息 -->
<!-- 一个订单关联查询出了多条订单明细,要使用collection进行映射。
collection:对关联查询到多条记录映射到集合对象中。
property:将关联查询到的多条记录映射到Orders中的哪个属性。
ofType:指定映射到哪个pojo类型的list集合-->
<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_num" property="itemsNum"/>
</collection>
</resultMap>
<resultMap type="com.zwd.mybatis.po.Orders" id="ordersResultMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置映射关联的用户信息 -->
<!-- association:用于映射关联查询单个对象的信息
property:要将关联查询的用户信息映射到Orders中的哪个属性-->
<association property="user" javaType="com.zwd.mybatis.po.User">
<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
mapper.java
/**
* 一对多:查询订单以及订单的详细信息
* @return
*/
public List<Orders> findOrdersUserDetails();
测试
@Test
public void findOrdersUserDetails() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
//List<OrdersCustom> orders = ordersMapper.findOrders(10);
List<Orders> orders1 = ordersMapper.findOrdersUserDetails();
System.out.println(orders1);
}
6、多对多
需求
查询用户(user)及用户购买的商品的信息(items)。
user和items的关系是多对多。
user–>orders:一对多;orders–>orderdetail:一对多;ordertail–>items:一对一。
在User类中添加一个Orders类的list;在Orders类中添加Orderdetail类的list;在Orderdetail中添加Items类的成员变量。
select
orders.*,
`user`.username,user.sex,`user`.address,
orderdetail.id orderdetail_id,orderdetail.items_num,
items.id items_id,items.`name` items_name,items.detail items_detail,items.price items_price
FROM user,orders,orderdetail,items
where `user`.id = orders.user_id and orderdetail.orders_id = orders.id
and orderdetail.items_id = items.id
mapper.xml
<!-- user->items:多对多关系映射 -->
<resultMap type="com.zwd.mybatis.po.User" id="userItemsResultMap">
<!-- 用户信息 -->
<id column="user_id" property="id"></id>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 订单信息 -->
<collection property="ordersList" ofType="com.zwd.mybatis.po.Orders">
<id column="id" property="id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 订单明细信息 -->
<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_num" property="itemsNum"/>
<!-- 商品信息 -->
<association property="items" javaType="com.zwd.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
<result column="items_price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<select id="findUserItems" resultMap="userItemsResultMap">
select
orders.*,
`user`.username,user.sex,`user`.address,
orderdetail.id orderdetail_id,orderdetail.items_num,
items.id items_id,items.`name` items_name,items.detail items_detail,items.price items_price
FROM user,orders,orderdetail,items
where `user`.id = orders.user_id and orderdetail.orders_id = orders.id
and orderdetail.items_id = items.id
</select>
mapper.java
/**
* 多对多:查询用户及用户购买的商品的商品信息
* @return
*/
public List<User> findUserItems();
测试
@Test
public void findUserItems() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
//List<OrdersCustom> orders = ordersMapper.findOrders(10);
List<User> user = ordersMapper.findUserItems();
System.out.println(user);
}
resultType和resultMap总结
7、延迟加载
resultMap
可以实现高级映射(使用association
、collection
实现一对一、一对多映射)。
association、collection具备延迟加载功能。
延迟加载:先从单表查询,需要时再从关联表去关联查询。大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
使用association实现延迟加载
1、通过association
标签的select
和column
实现延迟加载。(collection
实现方式一致)
<!-- 实现对用户信息的延迟加载
select:指定延迟加载需要执行的statement的id,如果该statement没有在当前文件中,需要指定namepace
column:订单信息中关联用户查询需要的关联字段;user_id -->
<association property="user" javaType="com.zwd.mybatis.po.User"
select="com.zwd.mybatis.mapper.UserMapper.findUserById" column="user_id">
</association>
2、另外还需要修改全局配置文件SqlMapConfig.xml
,添加设置。
设置项 | 描述 | 允许值 | 默认值 |
---|---|---|---|
lazyLoadingEnabled | 全局性设置懒加载。如果设为false,则所有相关联的都会被初始化加载 | true|false | false |
aggressiveLazyLoading | 当设置为true的时候,懒加载的对象可能被任何懒属性全部加载。否则每个属性都按需加载。 | true|false | true |
<!-- 配置懒加载 -->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
需求
查询订单并且关联查询用户信息。
同样需要在Orders类中添加User的成员变量。
mapper.xml、mapper.java
需要定义两个mapper的方法对应的statement。
1、只查询订单信息:select * from orders
2、关联查询用户信息:select * from user where user.id = #{orders.user_id}
通过上边查询到的订单信息中的user_id去关联查询用户信息。
2、关联查询用户信息:select * from user where user.id = #{orders.user_id}
userMapper.xml
<?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.zwd.mybatis.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
select *
from user
where user.id = #{id}
</select>
</mapper>
userMapper.java
package com.zwd.mybatis.mapper;
import com.zwd.mybatis.po.User;
public interface UserMapper {
/**
* 通过id查找用户
* @param id
* @return
*/
public User findUserById(int id);
}
1、只查询订单信息:select * from orders
OrdersCustomMapper.xml
<?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.zwd.mybatis.mapper.OrdersCustomMapper">
<!-- 延迟加载-->
<!-- 延迟加载的resultMap -->
<resultMap type="com.zwd.mybatis.po.Orders" id="ordersUserResultMap">
<!-- 对订单信息进行映射配置 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 实现对用户信息的延迟加载
select:指定延迟加载需要执行的statement的id,如果该statement没有在当前文件中,需要指定namepace
column:订单信息中关联用户查询需要的关联字段;user_id -->
<association property="user" javaType="com.zwd.mybatis.po.User"
select="com.zwd.mybatis.mapper.UserMapper.findUserById" column="user_id">
</association>
</resultMap>
<select id="findOrdersUser" resultMap="ordersUserResultMap">
select *
from orders
</select>
</mapper>
OrdersCustomMapper.java
package com.zwd.mybatis.mapper;
import java.util.List;
import com.zwd.mybatis.po.Orders;
public interface OrdersCustomMapper {
/**
* 使用延迟加载:查询订单及该订单的用户信息
* @return
*/
public List<Orders> findOrdersUser();
}
修改SqlMapConfig.xml
配置文件
<!-- 配置懒加载 -->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
测试
@Test
public void findOrdersUser() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersCustomMapper ordersCustomMapper = sqlSession.getMapper(OrdersCustomMapper.class);
//查询订单列表
List<Orders> ordersUser = ordersCustomMapper.findOrdersUser();
//遍历订单表
for (Orders order : ordersUser) {
//执行getUser()去查询用户信息,这里实现按需加载(懒加载)
User user = order.getUser();
System.out.println("order:");
System.out.println(order);
System.out.println("user:");
System.out.println(user);
}
}
8、查询缓存
什么是查询缓存:
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybatis提供一级缓存和二级缓存。
一级缓存是SqlSession
级别的缓存。在操作数据库是需要构造sqlSession对象,在对象中有一个数据结构(HashMap
)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap
)是互相不影响的。
二级缓存是mapper
级别的缓存,多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
为什么要用缓存:
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
8.1、一级缓存
一级缓存的工作原理
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(插入、更新、删除),mybatis就会清空sqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
一级缓存测试
/**
* 一级缓存测试
* @throws IOException
*/
@Test
public void testCache1() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//1、第一次查询id为1的用户
User user = userMapper.findUserById(1);
System.out.println(user);
userMapper.findUserById(10);
//2、修改用户
user.setUsername("张明明");
userMapper.updateUserById(user);
//更新用户:执行commit去清空当前sqlSession的一级缓存
sqlSession.commit();
//3、第二次查询id为1的用户
User user2 = userMapper.findUserById(1);
System.out.println(user2);
userMapper.findUserById(10);
//关闭sqlSession
sqlSession.close();
}
8.2、二级缓存
二级缓存的工作原理
1、首先开启mybatis的二级缓存
2、sqlSession1去查询用户id为1的用户信息,查询到的用户信息会存储到userMapper的二级缓存中。(需要将sqlSession1关闭后sqlSession1.close()
才会存储到二级缓存中)。
3、sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
二级缓存和一级缓存区别:二级缓存的范围更大,多个sqlSession可以共享一个userMapper的二级缓存区域。每一个namespace的mapper都有一个二级缓存区域。
开启二级缓存
Mybatis的二级缓存是mapper范围级别,除了在SqlMapConfig.xml
设置二级缓存的总开关,还要在具体的mapper.xml
中开启二级缓存。
在SqlMapConfig.xml
中设置二级缓存的总开关:
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
标签 | 描述 | 允许值 | 默认值 |
---|---|---|---|
cacheEnabled | 对在此配置文件下的所有cache进行全局性开/关设置 | true|false | true |
在Usermapper.xml
中添加配置:
<!-- 在本mapper的namespace下开启二级缓存 -->
<cache/>
pojo类实现序列化接口
二级缓存测试
/**
* 二级缓存测试
* @throws IOException
*/
@Test
public void testCache2() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
//1、sqlSession1查询id为1的用户
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
//这里执行关闭操作,将sqlSession中的数据写入到二级缓存中。
sqlSession1.close();
//2、使用sqlSession3执行提交操作
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user = userMapper3.findUserById(1);
user.setUsername("张晓敏");
userMapper3.updateUserById(user);
//执行提交操作,清空userMapper的二级缓存
sqlSession3.commit();
sqlSession3.close();
//3、sqlSession3查询id为1的用户
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
//这里执行关闭操作,将sqlSession中的数据写入到二级缓存中。
sqlSession2.close();
}
statement相关缓存参数
禁用二级缓存useCache
在statement中设置useCache="false"
可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
总结:针对每次查询都需要更新数据的sql,要设置成userCache=false
,禁用二级缓存。
<select id="findUserById" parameterType="int"
resultType="com.zwd.mybatis.po.User" useCache="false">
select *
from user
where user.id = #{id}
</select>
刷新缓存flushCache(清空二级缓存)
在mapper的同一个namespace中,如果有其他insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true"
属性,默认情况下为true。如果改成false则不会刷新,使用缓存是如果手动修改数据表中的查询数据会出现脏读。
<update id="updateUserById" parameterType="com.zwd.mybatis.po.User" flushCache="true">
update user
set user.username = #{username}
where user.id = #{id}
</update>
总结:一般执行完commit操作都需要刷新缓存,flushCache="true"
表示刷新缓存,这样可以避免数据库脏读。
cache标签的属性
flushInterval
:刷新间隔。可以被设置成任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,就是没有刷新间隔。缓存仅仅调用语句时刷新。
size
:引用数目。可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly
:只读。可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
8.3、mybatis和ehcache整合
EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
ehcache是一个分布式缓存框架。
分布式缓存
我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)
不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。
mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。
整合方法
mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。
mybatis提供二级缓存Cache接口,如下:
它的默认实现类:
通过实现Cache接口可以实现mybatis缓存数据通过其它缓存数据库整合,mybatis的特长是sql操作,缓存数据的管理不是mybatis的特长,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。
mybatis与ehcache整合步骤
加入ehcache包
添加缓存配置文件
classpath下添加:ehcache.xml
内容如下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- <diskStore path="F:\develop\ehcache" /> -->
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
属性说明:
diskStore
:指定数据在磁盘中的存储位置。
defaultCache
:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用defalutCache指定的的管理策略
以下属性是必须的:
maxElementsInMemory
- 在内存中缓存的element的最大数目
maxElementsOnDisk
- 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal
- 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk
- 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds
- 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds
- 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB
- 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent
- 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds
- 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy
- 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
开启ehcache缓存
EhcacheCache是ehcache对Cache接口的实现:
修改mapper.xml文件,在cache中指定EhcacheCache。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
根据需求调整缓存参数:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache参数maxElementsInMemory -->
<property name="maxEntriesLocalHeap" value="1000"/>
<!-- 同ehcache参数maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
9、mybatis和spring整合
9.1、整合思路
需要spring通过单例方式管理SqlSessionFactory。
spring和mybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession。(spring和mybatis整合自动完成)持久层的mapper都需要由spring进行管理。
9.2、整合环境准备
-
创建java项目
-
导入mybatis和spring的jar包:
- 项目结构
-
配置mybatis的总配置文件SqlMapConfig.xml(删除对数据源的配置,有spring来管理配置)
<?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="db.properties"></properties> <!-- 配置懒加载 --> <settings> <!-- 打开延迟加载的开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 将积极加载改为消极加载即按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> <!-- 开启二级缓存总开关 --> <setting name="cacheEnabled" value="true"/> </settings> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="SqlMap/User.xml"/> <mapper resource="com/zwd/mybatis/mapper/UserMapper.xml"/> <mapper class="com.zwd.mybatis.mapper.OrdersCustomMapper"/> <!-- <mapper resource="com/zwd/mybatis/mapper/OrdersCustomMapper.xml"/> --> </mappers> </configuration>
更改后:
<?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> <mappers> <mapper resource="SqlMap/User.xml"/> </mappers> </configuration>
9.3、ApplicationContext.xml
配置sqlSessionFactory
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置IOC -->
<!-- 配置数据源 -->
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
9.4、准备持久层-原始dao开发
- 项目结构
-
添加User.xml配置文件:
<?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="test"> <select id="findAllUsers" resultType="com.zwd.ssm.po.User"> select * from user </select> </mapper>
-
添加dao接口是实现类
接口:
package com.zwd.ssm.dao; import java.io.IOException; import java.util.List; import com.zwd.ssm.po.User; public interface UserDao { public List<User> findAllUsers() throws IOException; }
注意:dao的实现类需要继承SqlSessionDaoSupport
实现类继承SqlSessionDaoSupport类。该类提供设置sqlSessionFactory的方法和获取sqlSession的方法。
package com.zwd.ssm.dao;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import com.zwd.ssm.po.User;
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
// private SqlSessionFactory sqlSessionFactory;//让dao类继承SqlSessionDaoSupport类
@Override
public List<User> findAllUsers() throws IOException {
// SqlSession sqlSession = sqlSessionFactory.openSession();
//继承SqlSessionDaoSupport,通过getSqlSession()获取sqlSession
SqlSession sqlSession = getSqlSession();
List<User> users = sqlSession.selectList("test.findAllUsers");
return users;
}
}
package com.zwd.ssm.test;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zwd.ssm.dao.UserDao;
import com.zwd.ssm.po.User;
public class UserDaoImplTest {
@Test
public void testFindAllUsers() throws IOException {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
UserDao bean = (UserDao) ac.getBean("userDao");
List<User> users = bean.findAllUsers();
System.out.println(users);
}
}
9.5、准备持久层-mapper代理开发
- 项目结构
-
mapper.xml和mapper.java
<?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.zwd.ssm.mapper.UserMapper"> <select id="findAllUsers" resultType="com.zwd.ssm.po.User"> select * from user </select> </mapper>
package com.zwd.ssm.mapper; import java.util.List; import com.zwd.ssm.po.User; public interface UserMapper { public List<User> findAllUsers(); }
-
在SqlMapConfig.xml中配置mapper
<?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.zwd.ssm.po"/> </typeAliases> <mappers> <mapper resource="com/zwd/ssm/mapper/UserMapper.xml"/> </mappers> </configuration>
-
spring配置:applicationContext.xml
注意:mapper的配置:使用MapperFactoryBean的创建代理对象
注意mapper的配置:使用MapperFactoryBean的创建代理对象
<!-- 配置mapper:
MapperFactoryBean:根据mapper接口生产相应的代理对象 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.zwd.ssm.mapper.UserMapper"> </property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置IOC -->
<!-- 配置数据源 -->
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置mapper:
MapperFactoryBean:根据mapper接口生产相应的代理对象 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.zwd.ssm.mapper.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
</beans>
-
测试:
package com.zwd.ssm.test; import java.util.List; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zwd.ssm.mapper.UserMapper; import com.zwd.ssm.po.User; public class UserMapperTest { @Test public void test() { ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml"); UserMapper mapper = (UserMapper) ac.getBean("userMapper"); List<User> users = mapper.findAllUsers(); System.out.println(users); } }
改进
对于多个mapper代理对象的生成需要一个一个写。可以使用org.mybatis.spring.mapper.MapperScannerConfigurer
来批量生成mapper对象。
<!-- 统一扫描mapper创建代理对象:从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册。
遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录中。
自动扫描出来的mapper的bean的id为mapper类名首字母小写。
basePackage:指定扫描的包名:如果扫描多个包,每个包中间使用逗号分隔
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zwd.ssm.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
此处用MapperScannerConfigurer批量扫描配置后,SqlMapConfig.xml
可以不用再配置映射文件。
10、mybatis的逆向工程
- 创建java项目导包:
-
创建
generatorConfig.xml
文件:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="root"> </jdbcConnection> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </jdbcConnection> --> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="cn.zwd.ssm.po" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="cn.zwd.ssm.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="cn.zwd.ssm.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据库表 --> <table tableName="items"></table> <table tableName="orders"></table> <table tableName="orderdetail"></table> <table tableName="user"></table> <!-- <table schema="" tableName="sys_user"></table> <table schema="" tableName="sys_role"></table> <table schema="" tableName="sys_permission"></table> <table schema="" tableName="sys_user_role"></table> <table schema="" tableName="sys_role_permission"></table> --> <!-- 有些表的字段需要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration>
-
创建
GeneratorSqlmap.java
执行文件并执行。
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorSqlmap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("lib/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}