本节内容:
1)为什么要学习MyBatis
2)JDBC编程回顾和存在的问题
3)Mybatis入门案例
4)Mybatis Dao的开发方式 – 原始Dao开发方法 和 接口动态代理方式
5)SqlMapConfig.xml的配置解释
一)为什么要学习MyBatis
1)MyBatis是目前的主流的持久层框架:目前最主流的持久层框架为hibernate与mybatis,而且国内目前情况使用Mybatis的公司比hibernate要多。
2)Hibernate学习门槛不低,要精通门槛更高。门槛高在怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate缓存与数据加载策略方面需要你的经验和能力都很强才行。国内目前前的情况精通hibernate技术大牛非常少。
3)sql优化方面,Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。当然了,Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。说得更深入一些,如果有个查询要关联多张表,比如5张表,10张表时,而且,我们要取的字段只是其中几张表的部分字段。这时用hibernate时就会显得非常力不从心。就算用hibernate的sqlquery,后续的维护工作也会让人发狂。
二)JDBC编程回顾和存在的问题
JDBC编程步骤:
1)加载驱动
2)建立连接
3)创建sql语句和创建执行sql语句的对象
4)设置查询参数和执行sql语句
5)遍历结果集
6)释放资源
存在的问题:
- 频繁创建和打开、关闭数据连接,太消耗资源
- Sql语句存在硬编码,不利于维护
- Sql参数设置硬编码,不利于维护
- 结果集获取与遍历复杂(没有进行结果的封装),存在硬编码,不利于维护,期望能够查询后返回一个java对象
@Test
public void test(){
ResultSet resultSet = null ;
Connection connection = null;
PreparedStatement prepareStatement = null;
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//建立连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm_test?characterEncoding=utf-8",
"root", "123456");
//创建sql语句
String sql = "SELECT `id`, `username`, `birthday`, `sex`, `address` FROM `user` where username like ? ";
//创建执行sql语句的对象
prepareStatement = connection.prepareStatement(sql);
prepareStatement.setString(1, "%张%");
//执行sql语句
resultSet = prepareStatement.executeQuery();
//遍历结果集
while(resultSet.next()){
System.out.println(resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放资源
if(null != resultSet){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != prepareStatement){
try {
prepareStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
三)Mybatis入门案例
步骤一)导入jar包:
其实Mybatis真正的jar包只有一个,就是mybatis-3.2.7.jar
但是Mybatis依赖的jar包有9个,在导入的时候需要一并导入;
步骤二)导入Mybatis核心配置文件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/ssm_test?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
步骤三)导入log4j.properties配置文件
步骤四)实体类的User.java的编写
public class User {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private String uuid;
//省略set/get方法
}
步骤五)配置sql查询的映射文件:
配置模糊查询:
模糊查询的sql语句的写法有两种:
1) where username like #{username}
这时在查询的时候需要写成
List<User> userList = sqlSession.selectList("user.getUserByUsername", "%张%");
2) where username like '%${value}%'
这时在查询的时候需要写成,${}相当于一个字符串拼接符
如果入参为普通数据类型的时候,那么${}中只能够写value
List<User> userList = sqlSession.selectList("user.getUserByUsername", "张");
配置插入数据并返回id值:
添加用户并返回添加用户的id的方式:
方式一:在insert中配置 useGeneratedKeys="true" keyProperty="id"
useGeneratedKeys:标识插入使用自增id
keyProperty:与useGeneratedKeys配套使用,用于绑定主键接收的pojo属性
方式二:在insert标签内部配置selectKey标签
order指定selectKey在什么时候执行,获取主键是需要在执行insert之后,故为after
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
方式三:在数据库表新增一列uuid ,并在insert中配置selectKey
<selectKey keyProperty="uuid" order="BEFORE" resultType="string">
SELECT UUID();
</selectKey>
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">
<!--
namespace:命名空间,相当于java包,主要是用于隔离sql语句,后续有重要作用
#{}:是占位符,相当于jdbc的?
${}:是字符串拼接指令,如果入参为普通数据类型时,{只能写value}
-->
<mapper namespace="user">
<!-- 根据用户ID查询用户信息 -->
<select id="getUserById" parameterType="int" resultType="com.itheima.pojo.User">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
WHERE
`id` = #{id}
</select>
<!-- 根据用户名查找用户列表 -->
<select id="getUserByUsername" parameterType="string" resultType="com.itheima.pojo.User">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
WHERE
<!-- `username` LIKE #{username} -->
<!-- ${}:是字符串拼接指令,如果入参为普通数据类型时,{只能写value} -->
`username` LIKE '%${value}%'
</select>
-----------------------------------------------------------------------------
模糊查询的sql语句的写法有两种:
1) where username like #{username}
这时在查询的时候需要写成
List<User> userList = sqlSession.selectList("user.getUserByUsername", "%张%");
2) where username like '%${value}%'
这时在查询的时候需要写成,${}相当于一个字符串拼接符
如果入参为普通数据类型的时候,那么${}中只能够写value
List<User> userList = sqlSession.selectList("user.getUserByUsername", "张");
-----------------------------------------------------------------------------
<!-- 添加用户 -->
<insert id="insertUser" parameterType="com.itheima.pojo.User" >
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`)
VALUES
(#{username},
#{birthday},
#{sex},
#{address});
</insert>
-----------------------------------------------------------------------------
添加用户并返回添加用户的id的方式:
方式一:在insert中配置 useGeneratedKeys="true" keyProperty="id"
useGeneratedKeys:标识插入使用自增id
keyProperty:与useGeneratedKeys配套使用,用于绑定主键接收的pojo属性
方式二:在insert标签内部配置selectKey标签
order指定selectKey在什么时候执行,获取主键是需要在执行insert之后,故为after
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
方式三:在数据库表新增一列uuid ,并在insert中配置selectKey
<selectKey keyProperty="uuid" order="BEFORE" resultType="string">
SELECT UUID();
</selectKey>
-----------------------------------------------------------------------------
<!-- 方式一:添加用户,返回用户的id -->
<!-- <insert id="insertUserReturnId" parameterType="com.itheima.pojo.User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`)
VALUES
(#{username},
#{birthday},
#{sex},
#{address});
</insert> -->
<!--方式二:在insert标签内部配置selectKey标签-->
<insert id="insertUserReturnId" parameterType="com.itheima.pojo.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`)
VALUES
(#{username},
#{birthday},
#{sex},
#{address});
</insert>
<!-- 方式三:在数据库表新增一列uuid -->
<insert id="insertUserUUID" parameterType="com.itheima.pojo.User">
<!-- 记得在原来的pojo类中增加set方法,不然会报错-->
<selectKey keyProperty="uuid" order="BEFORE" resultType="string">
SELECT UUID();
</selectKey>
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`,
`uuid`)
VALUES
(#{username},
#{birthday},
#{sex},
#{address},
#{uuid});
</insert>
<!-- 修改用户 -->
<update id="updateUser" parameterType="com.itheima.pojo.User">
UPDATE `user`
SET
`username` = #{username},
`birthday` = #{birthday} ,
`sex` = #{sex} ,
`address` = #{address} ,
`uuid` = #{uuid}
WHERE
`id` = #{id} ;
</update>
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="int">
DELETE FROM `user`
WHERE
`id` = #{id} ;
</delete>
</mapper>
步骤六)加载映射文件,在SqlMapConfig.xml中配置mappers节点
<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/ssm_test?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
步骤七)SqlSessionFactoryUtil的抽取
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
//单例模式,只有一个SqlSessionFactory
static{
try {
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = ssfb.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取sqlSessionFactory对象
* @return
*/
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
步骤八)测试类的编写:
public class UsetTest {
@Test //根据用户ID查询用户信息
public void getUserById() throws Exception{
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = ssfb.build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
//参数一:在User.xml映射配置文件中配置的唯一标识 参数二:参数
User user = session.selectOne("user.getUserById", 10);
System.out.println(user);
//释放资源
session.close();
}
@Test //根据用户名查找用户列表
public void getUserByUsername(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
/*List<User> userList = sqlSession.selectList("user.getUserByUsername", "%张%");*/
List<User> userList = sqlSession.selectList("user.getUserByUsername", "张");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test //添加用户
public void insertUser(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("深圳宝安");
user.setBirthday(new Date());
user.setSex("0");
user.setUsername("李四");
//session.insert("insertUser", user); //好像不写包也是可以正常执行的
session.insert("user.insertUser", user);
session.commit();//需要手动提交事务,不然会自动进行回滚事务
session.close();
}
@Test //添加用户,返回用户id
public void insertUserReturnId(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("深圳宝安3");
user.setBirthday(new Date());
user.setSex("0");
user.setUsername("李四3");
session.insert("user.insertUserReturnId", user);
System.out.println("user1:"+user);//这里虽然是在提交之前,但是也是可以拿到id值的,只要进行了插入
session.commit();//需要手动提交事务,不然会自动进行回滚事务
System.out.println("user2:"+user);
session.close();
}
@Test //添加用户,返回用户id
public void insertUserUUID(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setAddress("深圳宝安4");
user.setBirthday(new Date());
user.setSex("0");
user.setUsername("李四4");
session.insert("user.insertUserUUID", user);
System.out.println("user1:"+user);//这里虽然是在提交之前,但是也是可以拿到id值的,只要进行了插入
session.commit();//需要手动提交事务,不然会自动进行回滚事务
System.out.println("user2:"+user);
session.close();
}
@Test
public void updateUser(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
User user = new User();
user.setId(32);
user.setAddress("深圳宝安5");
user.setBirthday(new Date());
user.setSex("0");
user.setUsername("李四5");
session.insert("user.updateUser", user);
session.commit();//需要手动提交事务,不然会自动进行回滚事务
System.out.println("user2:"+user);
session.close();
}
@Test
public void deleteUser(){
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
session.insert("user.deleteUser", 32);
session.commit();//需要手动提交事务,不然会自动进行回滚事务
session.close();
}
}
四)Mybatis Dao的开发方式 – 原始Dao开发方法 和 接口动态代理方式
4.1)原始的Dao开发方法:
使用原有的核心配置文件SqlMapConfig.xml和sql映射文件User.xml
步骤一)新建UserDao接口 和 UserDaoImpl实现类:
public class UserDaoImpl implements UserDao {
@Override
public User getUserById(Integer id) {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
User user = sqlSession.selectOne("user.getUserById", id);
sqlSession.close();
return user;
}
@Override
public List<User> getUserByUserName(String name) {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
List<User> userList = sqlSession.selectList("user.getUserByUsername", "张");
sqlSession.close();
return userList;
}
@Override
public void insertUser(User user) {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
sqlSession.insert("user.insertUser", user);
sqlSession.commit();
sqlSession.close();
}
}
步骤二)使用dao测试:
在接口上面,右键 - new - JUnit Test Case
public class UserDaoTest {
@Test
public void testGetUserById() {
UserDao userDao = new UserDaoImpl();
User user = userDao.getUserById(31);
System.out.println(user);
}
@Test
public void testGetUserByUserName() {
UserDao userDao = new UserDaoImpl();
List<User> userList = userDao.getUserByUserName("张");
System.out.println(userList.size());
System.out.println(userList);
}
@Test
public void testInsertUser() {
UserDao userDao = new UserDaoImpl();
User user = new User();
user.setAddress("深圳");
user.setBirthday(new Date());
user.setSex("0");
user.setUsername("赵四");
userDao.insertUser(user);
}
}
4.1)动态代理Dao开发方式 – 只需要UserDao的接口,不需要编写这个接口的实现类了
开发规则:
- namespace必需是接口的全路径名
- 接口的方法名必需与映射文件的sql id一致
- 接口的输入参数必需与映射文件的parameterType类型一致
- 接口的返回类型必须与映射文件的resultType类型一致
sql映射配置文件)UserMap.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">
<!--
1. namespace必需是接口的全路径名
2. 接口的方法名必需与映射文件的sql id一致
3. 接口的输入参数必需与映射文件的parameterType类型一致
4. 接口的返回类型必须与映射文件的resultType类型一致,不区分大小写 -->
<mapper namespace="com.itheima.dao.UserDao">
<!-- 根据用户ID查询用户信息 -->
<select id="getUserById" parameterType="int" resultType="uSEr">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
WHERE
`id` = #{id}
</select>
<!-- 根据用户名查找用户列表 -->
<select id="getUserByUserName" parameterType="string" resultType="user">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM
`user`
WHERE
<!-- `username` LIKE #{username} -->
<!-- ${}:是字符串拼接指令,如果入参为普通数据类型时,{只能写value} -->
`username` LIKE '%${value}%'
</select>
<!-- 添加用户 -->
<insert id="insertUser" parameterType="user" >
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`)
VALUES
(#{username},
#{birthday},
#{sex},
#{address});
</insert>
</mapper>
接口)UserDao.java
public interface UserDao {
/**根据用户ID查询用户信息
* @param id
* @return
*/
User getUserById(Integer id);
/**
* 根据用户名查找用户列表
* @param name
* @return
*/
List<User> getUserByUserName(String name);
/**
* 添加用户
* @param user
*/
void insertUser(User user);
}
测试动态代理)只需要UserDao的接口,不需要编写这个接口的实现类了
public class UserDaoTest {
@Test
public void testGetUserById() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
//获得代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);//这个接口是没有实现类的。
User user = userDao.getUserById(31);
System.out.println(user);
// 关闭资源
sqlSession.close();
}
@Test
public void testGetUserByUserName() { //接口的方法名必需与映射文件的sql id一致,区分大小写
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserByUserName("张");
System.out.println(userList.size());
System.out.println(userList);
// 关闭资源
sqlSession.close();
}
@Test
public void testInsertUser() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setAddress("不知道");
user.setUsername("123");
userDao.insertUser(user);
sqlSession.commit();
sqlSession.close();
}
}
五)SqlMapConfig.xml的配置解释
1)properties:
properties方式一:在该配置文件中定义properties
properties方式二:引入工程下面的properties文件
2)别名:typeAliases
<typeAliases>
<!-- 单个别名定义 -->
<!-- <typeAlias type="com.itheima.mybatis.pojo.User" alias="user"/> -->
<!-- 别名包扫描器(推荐使用此方式),整个包下的类都被定义别名,别名为类名,不区分大小写-->
<package name="com.itheima.mybatis.pojo"/>
</typeAliases>
3)mappers
<mappers>
<!-- 第一种方式,加载 resource-->
<mapper resource="mapper/user.xml"/>
<!-- <mapper resource="mapper/UserMapper.xml"/> -->
<!-- 第二种方式,class扫描器要求:
1、映射文件与接口同一目录下
2、映射文件名必需与接口文件名称一致
-->
<!-- <mapper class="com.itheima.mybatis.mapper.UserMapper"/> -->
<!-- 第三种方式,包扫描器要求(推荐使用此方式):
1、映射文件与接口同一目录下
2、映射文件名必需与接口文件名称一致
-->
<package name="com.itheima.mybatis.mapper"/>
</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>
<!--properties方式一:在该配置文件中定义properties
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm_test?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties> -->
<!--properties方式二:引入工程下面的properties文件 -->
<properties resource="jdbc.properties"></properties>
<typeAliases>
<!-- <typeAlias type="com.itheima.pojo.User" alias="user"/> -->
<!-- 别名包扫描器(推荐使用此方式),整个包下的类都被定义别名,别名为类名,不区分大小写 -->
<package name="com.itheima.pojo"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- <mapper resource="sqlmap/UserMapper.xml"/> -->
<!-- 包扫描器规则:
1、映射文件与接口必需同一目录下
2、接口名称必需与映射文件名称一致
-->
<package name="com.itheima.dao"/>
</mappers>
</configuration>