1、mybatis的框架
解释:
1、 mybatis配置SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂(其是一个接口)
3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中
一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement
在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement
在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
2、mybatis所需的jar和依赖的jar包
2.1下载mybatis
mybaits的代码由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases
目录结构:
mybatis-3.2.7.jar----mybatis的核心包
lib----mybatis的依赖包
mybatis-3.2.7.pdf----mybatis使用手册
3、搭建mybatis工程
3.1加入mybatis核心包、依赖包、数据驱动包。
3.2配置log4j.properties,与classpath下
因为mybatis默认使用log4j作为输出日志信息。
# Global logging configuration 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
3.3创建SqlMapConfig.xml
在classpath下创建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/mybaits?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="admin" /> </dataSource> </environment> </environments> </configuration>
注意:SqlMapConfig.xml是mybatis核心配置文件,上边文件的配置内容为数据源、事务管理和连接池。
4、小实例
4.1po类
Po类作为mybatis进行sql映射使用,po类通常与数据库表对应,User.java如下:
数据库的表:
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=32 DEFAULT CHARSET=utf8;
对应的user的po类
public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 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 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; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
4.2创建sql映射文件
在classpath下的sqlmap目录下创建sql映射文件Users.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"> </mapper>
namespace:类似于c++中的namespace和Java中的package,用于隔离sql语句
4.3加载映射文件
mybatis框架需要加载映射文件,将Users.xml添加在SqlMapConfig.xml,如下:
<mappers> <mapper resource="sqlmap/User.xml"/> </mappers>
4.3实现提供ID查询用户
4.3.1修改映射文件
在user.xml中添加:
<!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select>
注意:
1、id:sql语句唯一表示
2、parameterType:指定传入参数类型,对应pojo属性的类型
3、resultType:返回结果类型
4、#{}占位符,起到站位的作用,如果传入的是基本类型(string、long、int、boolean、float等),
那么#{}中的变量名称可以是随意写,一般写属性
4.3.2测试程序
public class UserTest { @Test public void testFindUserById() throws Exception{ String resource = "SqlMapConfig.xml"; //通过流将核心配置文件读取进来 InputStream inputStream = Resources.getResourceAsStream(resource); //通过核心配置文件输入流来创建会话工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //通过工厂创建会话 SqlSession session = factory.openSession(); //第一个参数:所调用的sql语句:namespece.+sql的id User user= session.selectOne("test.findUserById", 1); System.out.println(user); session.close(); } }
4.4根据用户名查询用户信息
4.4.1修改映射文件
在user.xml中添加:
<select id="findUserByUserName" parameterType="java.lang.String" resultType="com.wang.pojo.User"> SELECT * from user where username like ‘%${value}%‘ </select>
注意:
1、如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所映射的文件一个配置成集合范型的类型
2、${}拼接符,字符串原样拼接,如果传入的是基本类型(string、long、int、boolean、float等),
那么%{}中的变量名称必须是value
但有sql注入的风险
4.4.2测试程序
@Test public void testFindUserByUserName()throws Exception{ String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = factory.openSession(); List<User> selectList= sqlSession.selectList("test.findUserByUserName", "王"); System.out.println(selectList); }
4.5总结
4.5.1#{}和${}
a、#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,
#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
#{}:如果传入的pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性.....
b、${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,
如果parameterType传输单个简单类型值,${}括号中只能是value。
${}:如果传入的pojo类型,那么${}中的变量名称必须是pojo中对应的属性.属性.属性.....
4.5.2parameterType和resultType
parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
4.5.2selectOne和selectList
selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常。
selectList可以查询一条或多条记录。
5、增删改查的实现
5.1增加(并且返回自增的主键id值)
5.1.1 映射文件:
在user.xml中添加:
<insert id="insertUser" parameterType="com.wang.pojo.User"> <!-- 执行SELECT LAST_INSERT_ID()数据库函数,返回自增主键 keyProperty:将返回的主键放入传入参数的Id中保存 order:当前函数相对于insert语句的执行顺序,在insert前是BEFORE之后是AFTER resultType:ID的类型,就是keyProperty中的类型 --> <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>
注意:添加selectKey实现将主键返回
keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
5.1.2测试程序:
//自动开启事务,但是要自己手动提交事务 @Test public void testInsertUser()throws Exception{ String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = factory.openSession(); User user = new User(); user.setSex("1"); user.setAddress("北京"); Date date = new Date(); user.setBirthday(date); user.setUsername("大江"); System.err.println(user.getId()); session.insert("test.insertUser", user); //提交事务(没有不插入数据库) session.commit(); System.err.println(user.getId()); session.close(); }
注意:mybatis是自动开启事务,但是要手动提交事务
衍生:UUID的主键获取
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"> <selectKey resultType="java.lang.String" order="BEFORE" keyProperty="id"> select uuid() </selectKey> insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address}) </insert>
使用的是 order="BEFORE" 和select uuid()
5.2 删除用户
5.2.1 映射文件:
在user.xml中添加:
<delete id="deleteUserById" parameterType="java.lang.Integer"> delete from user where id=#{id} </delete
5.2.2测试
@Test public void testDeleteUserById()throws Exception{ String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = factory.openSession(); session.delete("test.deleteUserById", 29); session.commit(); session.close(); }
5.3修改
5.3.1修改user.xml
<update id="updateById" parameterType="com.wang.pojo.User"> update user set username=#{username} where id=#{id} </update>
5.3.2添加测试
@Test public void testUpdateUserById()throws Exception{ String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = factory.openSession(); User user = new User(); user.setId(28); user.setUsername("lllll"); session.update("test.updateById", user); session.commit(); session.close(); }
6 SqlSession在讨论
6.1SqlSession的使用范围
SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等。
通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。
6.2SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,
因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
6.3SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,
一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
6.4SqlSession
SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作方法。
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
7 DAO层的开发:
7.1原生(自定义实现)DAO层的开发
7.1.1 sql映射文件
<?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隔离 --> <mapper namespace="test"> <!-- id:sql语句唯一表示 parameterType:指定传入参数类型,对应pojo属性的类型 resultType:返回结果类型 #{}占位符,起到站位的作用,如果传入的是基本类型(string、long、int、boolean、float等), 那么#{}中的变量名称可以是随意写,一般写属性 --> <select id="findUserById" parameterType="java.lang.Integer" resultType="com.wang.pojo.User"> SELECT * from user WHERE id =#{id} </select> <!-- 如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所映射的文件一个配置成集合范型的类型 ${}拼接符,字符串原样拼接,如果传入的是基本类型(string、long、int、boolean、float等), 那么%{}中的变量名称必须是value 注意:有sql注入的风险 --> <select id="findUserByUserName" parameterType="java.lang.String" resultType="com.wang.pojo.User"> SELECT * from user where username like ‘%${value}%‘ </select> </mapper>
7.1.2 dao层
public interface UserDao { public User findUserById(Integer id); public List<User> findUserByName(String userName); }
dao层的实现层
public class UserDaoImpl implements UserDao { private SqlSessionFactory factory; public UserDaoImpl(SqlSessionFactory factory) { this.factory = factory; } @Override public User findUserById(Integer id) { //sesion是线程不安全的,最佳在方法体内 SqlSession session = factory.openSession(); User user = session.selectOne("test.findUserById", id); return user; } @Override public List<User> findUserByName(String userName) { SqlSession sqlSession = factory.openSession(); List<User> selectList= sqlSession.selectList("test.findUserByUserName",userName); return selectList; } }
7.1.3测试
public class UserDaoTest { private SqlSessionFactory factory; @Before public void setUp()throws Exception { String resource = "SqlMapConfig.xml"; //通过流将核心配置文件读取进来 InputStream inputStream = Resources.getResourceAsStream(resource); //通过核心配置文件输入流来创建会话工厂 factory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void findUserById() throws Exception{ UserDao userDao = new UserDaoImpl(factory); System.out.println(userDao.findUserById(1)); } @Test public void findUserByName() throws Exception{ UserDao userDao = new UserDaoImpl(factory); List<User> users =userDao.findUserByName("王"); System.out.println(users); } }
存在的问题:
原始Dao开发中存在以下问题:
1、Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
2、 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护。
7.2 Mapper动态代理方式
7.2.1开发规范
Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类全路径相同。
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同(sql语句的id)
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
注意:mapper类名与mapper映射文件名相同
7.2.2Mapper.xml(映射文件)
定义mapper映射文件UserMapper.xml(内容同Users.xml),需要修改namespace的值为 UserMapper接口路径。
将UserMapper.xml放在classpath 下mapper目录下。
<?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="cn.itcast.mybatis.mapper.UserMapper"> <!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 自定义条件查询用户列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User"> select * from user where username like ‘%${value}%‘ </select> <!-- 添加用户 --> <insert id="insertUser" parameterType="cn.itcast.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> </mapper>
7.2.3Mapper.java(接口文件)
Public interface UserMapper { //根据用户id查询用户信息 public User findUserById(int id) throws Exception; //查询用户列表 public List<User> findUserByUsername(String username) throws Exception; //添加用户信息 public void insertUser(User user)throws Exception; }
接口定义有如下特点:
1、 Mapper接口方法名和Mapper.xml中定义的statement的id相同
2、 Mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同
3、 Mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同
7.2.4 加载UserMapper.xml文件
<mapper class="com.wang.mapper.UserMapper"/>
注意:
使用类扫描的方式引入Mapper接口
1、接口名称和映射文件名称除扩展名外要完全相同
2、接口和映射文件要放在同一个目录下
或:
<package name="com.wang.mapper"/>
注意:
使用包扫描的方式批量引入Mapper接口
1、接口名称和映射文件名称除扩展名外要完全相同
2、接口和映射文件要放在同一个目录下
7.2.5测试
public class UserMapperTest { private SqlSessionFactory factory; @Before public void setUp()throws Exception { String resource = "SqlMapConfig.xml"; //通过流将核心配置文件读取进来 InputStream inputStream = Resources.getResourceAsStream(resource); //通过核心配置文件输入流来创建会话工厂 factory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void findUserById() throws Exception{ SqlSession session = factory.openSession(); //通过getMapper实例化接口 UserMapper userMapper = session.getMapper(UserMapper.class); User user= userMapper.findUserById(1); System.err.println(user); } @Test public void findUserByUserName() throws Exception{ SqlSession session = factory.openSession(); //通过getMapper实例化接口 UserMapper userMapper = session.getMapper(UserMapper.class); List<User> users= userMapper.findUserByUserName("王"); System.err.println(users); } @Test public void insertUser() throws Exception{ SqlSession session = factory.openSession(); //通过getMapper实例化接口 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setAddress("南京"); user.setSex("1"); user.setBirthday(new Date()); user.setUsername("孙"); userMapper.insertUser(user); session.commit(); } }
7.3总结
1、selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,
如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
2、 namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,
输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
8 SqlMapConfig.xml配置文件
8.1 配置内容
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
8.2 properties
SqlMapConfig.xml可以引用java属性文件中的配置信息如下:
在classpath下定义db.properties文件,
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybaits?characterEncoding=utf-8 jdbc.username=root jdbc.password=admin
SqlMapConfig.xml引用如下:
<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.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments>
注意: MyBatis 将按照下面的顺序来加载属性:
1、在 properties 元素体内定义的属性首先被读取。
2、然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
8.3 typeAliases(类型别名)
8.3.1 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 |
map |
Map |
8.3.2自定义别名
在SqlMapConfig.xml中配置:
<typeAliases> <!-- 定义单个pojo类别名 type=类的全路径名称 alias=别名 --> <typeAlias type="com.wang.pojo.User" alias="User"/> <!-- 使用包扫描的方式批量定义别名 别名就是类名,不区分大小写 --> <package name="com.wang.pojo"/> </typeAliases>
因此:
<select id="findUserByUserName" parameterType="string" resultType="User"> SELECT * from user where username like ‘%${value}%‘ </select>
8.4mappers(映射器)
Mapper配置的几种方法:
8.1. <mapper resource=" " />
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
8.2 <mapper class=" " />
使用mapper接口类路径
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
8.3 <package name=""/>
注册指定包下的所有mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。