Mybatis开发dao
Mybatis在项目中主要使用的地方就是开发dao(数据访问层),
有两种方式:原始dao开发方式、mapper代理开发方式(推荐)。
需求:
1、 根据用户ID来查询用户信息;
2、 根据用户名称来模糊查询用户信息列表;
3、添加用户;一、原始dao开发方式
1、思路
需要写dao接口和dao实现类。
2、编程步骤
1、 根据需求创建po类
2、 编写全局配置文件
3、 根据需求编写映射文件
4、 加载映射文件
5、 编写dao接口
6、 编写dao实现类
7、 编写测试代码
3、程序编写
在测试代码中,有大量的重复代码。所以我们第一反应就是想给它抽取出共性的部分,
但是SqlSession、SqlSessionFactory、SqlSessionFactoryBuilder有着各自的生命周期,
因为这些生命周期的不同,抽取时要有针对性的处理。
它的作用只是通过配置文件创建SqlSessionFactory,所以只要创建出SqlSessionFactory,它就可以销毁了。
所以说,它的生命周期是在方法之内。
SqlSessionFactory
它的作用是创建SqlSession的工厂,工厂一旦创建,除非应用停掉,不要销毁。
所以说它的生命周期是在应用范围内。这里可以通过单例模式来管理它。
在mybatis整合spring之后,最好的处理方式是把SqlSessionFactory交由spring来做单例管理。
SqlSession
SqlSession是一个面向用户(程序员)的接口,它的默认实现是DefaultSqlSession。
Mybatis是通过SqlSession来操作数据库的。SqlSession中不仅包含要处理的SQL信息,还包括一些数据信息,
所以说它是线程不安全的,因此它最佳的生命周期范围是在方法体之内。
UserDao:
package com.hcx.mybatis.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.hcx.mybatis.po.User;
/**
*
* <p>
* Title: UserDao
*/
public interface UserDao {
// 1、 根据用户ID查询用户信息
public User findUserById(int id) throws Exception;
// 2、 根据用户名称模糊查询用户列表
public List<User> findUsersByName(String name) throws Exception;
// 3、 添加用户
public void insertUser(User user) throws Exception;
}
UserDaoImpl:
需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
要注意SqlSession和SqlSessionFactory的生命周期。
package com.hcx.mybatis.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.hcx.mybatis.po.User;
/**
* Title: UserDaoImpl
*/
public class UserDaoImpl implements UserDao {
// 依赖注入
private SqlSessionFactory sqlSessionFactory;
//使用构造方法来初始化SqlSessionFactory
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
// 通过工厂在方法内部获取sqlsession,这样可以避免线程安全问题
SqlSession sqlSession = sqlSessionFactory.openSession();
// 调用SqlSession的增删改查方法
// 第一个参数:表示statement的唯一标示
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println(user);
// 关闭资源
sqlSession.close();
return user;
}
@Override
public List<User> findUsersByName(String name) {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 调用SqlSession的增删改查方法
// 第一个参数:表示statement的唯一标示
List<User> list = sqlSession.selectOne("test.findUsersByName", name);
System.out.println(list);
// 关闭资源
sqlSession.close();
return list;
}
@Override
public void insertUser(User user) {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 调用SqlSession的增删改查方法
// 第一个参数:表示statement的唯一标示
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId());
// 提交事务
sqlSession.commit();
// 关闭资源
sqlSession.close();
}
}
UserDaoTest:
public class UserDaoTest {
//声明全局的SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// 1、读取配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据配置文件创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
//构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
//调用UserDao对象的方法
User user = userDao.findUserById(1);
System.out.println(user);
}
@Test
public void testFindUsersByName() {
//构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
//调用UserDao对象的方法
List<User> list = userDao.findUsersByName("小明");
System.out.println(list);
}
@Test
public void testInsertUser() {
//构造UserDao对象
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
//构造User对象
User user = new User();
user.setUsername("东哥3");
user.setAddress("清河宝盛西里3");
//调用UserDao对象的方法
userDao.insertUser(user);
System.out.println(user.getId());
}
}
4、问题总结
原始dao开发存在一些问题:
存在一定量的模板代码。比如:通过SqlSessionFactory创建SqlSession;调用SqlSession的方法操作数据库;关闭Sqlsession。
存在一些硬编码。调用SqlSession的方法操作数据库时,需要指定statement的id,这里存在了硬编码。
二、Mapper代理开发方式
Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可。Mybatis会自动的为mapper接口生成动态代理实现类。
不过要实现mapper代理的开发方式,需要遵循一些开发规范。
1、开发规范
1、 mapper接口的全限定名要和mapper映射文件的namespace的值相同。
2、 mapper接口的方法名称要和mapper映射文件中的statement的id相同;
3、 mapper接口的方法参数只能有一个,且类型要和mapper映射文件中statement的parameterType的值保持一致。
4、 mapper接口的返回值类型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致;
通过规范式的开发mapper接口,可以解决原始dao开发当中存在的问题:
1、 模板代码已经去掉;
2、 剩下去不掉的操作数据库的代码,其实就是一行代码。这行代码中硬编码的部分,通过第一和第二个规范就可以解决。
2、编程步骤
1、 根据需求创建po类
2、 编写全局配置文件
3、 根据需求编写映射文件
4、 加载映射文件
5、 编写mapper接口
6、 编写测试代码
3、程序编写
重新定义mapper映射文件UserMapper.xml(内容同Users.xml,除了namespace的值),放到新创建的目录mapper下。
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:此时用mapper代理方式,它的值必须等于对应mapper接口的全限定名 -->
<mapper namespace="cn.hcx.mybatis.mapper.UserMapper">
<!-- 根据用户ID,查询用户信息 -->
<!--
[id]:statement的id,要求在命名空间内唯一
[parameterType]:入参的java类型,可是是简单类型、POJO、HashMap
[resultType]:查询出的单条结果集对应的java类型
[#{}]: 表示一个占位符?
[#{id}]:表示该占位符待接收参数的名称为id。注意:如果参数为简单类型时,#{}里面的参数名称可以是任意定义
-->
<select id="findUserById" parameterType="int" resultType="cn.hcx.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
<!-- 根据用户名称模糊查询用户信息列表 -->
<!--
[${}]:表示拼接SQL字符串,即不加解释的原样输出
[${value}]:表示要拼接的是简单类型参数。
注意:
1、如果参数为简单类型时,${}里面的参数名称必须为value
2、${}会引起SQL注入,一般情况下不推荐使用。但是有些场景必须使用${},比如order by ${colname}
-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="cn.hcx.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
<!-- 添加用户之自增主键返回(selectKey方式) -->
<!--
[selectKey标签]:通过select查询来生成主键
[keyProperty]:指定存放生成主键的属性
[resultType]:生成主键所对应的Java类型
[order]:指定该查询主键SQL语句的执行顺序,相对于insert语句,此时选用AFTER
[last_insert_id]:MySQL的函数,要配合insert语句一起使用
-->
<insert id="insertUser" parameterType="cn.hcx.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES (#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>
(2)加载mapper映射文件
<!-- 加载mapper -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
(3)编写mapper接口
public interface UserMapper {
//根据用户ID来查询用户信息
public User findUserById(int id);
//根据用户名称来模糊查询用户信息列表
public List<User> findUsersByName(String username);
//添加用户
public void insertUser(User user);
}
(4)编写测试代码
public class UserMapperTest {
// 声明全局的SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// 1、读取配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2、根据配置文件创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用mapper对象的方法
User user = userMapper.findUserById(1);
System.out.println(user);
// 关闭SqlSession
sqlSession.close();
}
@Test
public void testFindUsersByName() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用mapper对象的方法
List<User> list = userMapper.findUsersByName("小明");
System.out.println(list);
// 关闭SqlSession
sqlSession.close();
}
@Test
public void testInsertUser() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//构造User对象
User user = new User();
user.setUsername("东哥4");
user.setAddress("清河宝盛西里4");
// 调用mapper对象的方法
userMapper.insertUser(user);
System.out.println(user.getId());
//执行SqlSession的commit操作
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
}