【Java】DAO模式简介
DAO(Data Access Object,数据访问对象),主要的功能是用于进行数据操作的,在程序的标准开发框架中属于数据层的操作。
数据开发结构:
资源层是数据库的操作层,里面可以进行各种数据库的存储,但是这些数据存储的时候肯定是依靠SQL语句,数据层通过一个专门的数据库组件完成对数据库的操作
业务层是整个项目的核心
DAO组成
DatabaseConnection:专门负责数据库打开与关闭操作的类
VO:主要由属性,setter, getter方法组成,VO类中的属性与表中的字段相对应,每一个VO类的对象都表示表中的每一条记录;
DAO:主要定义操作的接口,定义一系列数据库的原子性操作,例如增删改查等;
Impl: DAO接口的真实实现类,主要完成具体数据库操作,但不负责数据库的打开和关闭;
Proxy:代理实现类,主要完成数据库的打开和关闭并且调用真实实现类对象的操作;
Factory: 工厂类,通过工厂类取得一个DAO的实例化对象。
对于包的命名:
在使用DAO时对包有严格的命名
- 数据库连接: xxx.dbc.DatabaseConnection
- DAO接口: xxx.dao.IXxxDAO
- DAO接口真实实现类:xxx.dao.impl.XxxDAOImpl
- DAO接口代理实现类:xxx.dao.proxy.XxxDAOProxy
- VO类: xxx.vo.Xxx, VO命名要与表的命名一致
- 工厂类:xxx.factory.DAOFactory.
实施DAO模式 (Implementing DAO pattern)**
使用上述组件,让我们尝试实现DAO模式。 我们将在这里使用3个组件:
1.从一层转移到另一层的book模型。
2.bookdao接口提供了灵活的设计和要实现的API。
3.BookDaoImpl具体类,它是bookdao接口的实现
DAO模式的优势**
使用DAO模式有很多优点。 让我们在这里声明其中一些:
在更改持久性机制时,服务层甚至不必知道数据来自何处。 例如,如果您正在考虑从使用MySQL过渡到MongoDB,则所有更改仅需要在DAO层中完成。
DAO模式强调应用程序不同组件之间的低耦合。 因此,View层不依赖于DAO层,而仅Service层依赖于DAO层,即使依赖于接口,也不依赖于具体的实现。
由于持久性逻辑是完全独立的,因此为单个组件编写单元测试要容易得多。 例如,如果将JUnit和Mockito用于测试框架,则可以轻松模拟应用程序的各个组件。
当我们以DAO模式使用接口时,它还强调了“使用接口代替实现”的风格,这是一种出色的OOPs编程风格。
什么情况下产生模式
当我们的业务和数据都要处理的时候,但是业务和数据都是不同的实现方式,但是它们之间又有联系,所以这里就产生了解耦概念。单独封装一个类来处理数据的代码
当处理业务和数据的代码在一起的时候,会出现以下情况:
1.维护困难因为每该一次数据都会影响业务代码。
2.代码复用比较低
3.编写业务层代码的人员必须要懂各种数据保存方式
MyBatis开发Dao模式过程**
Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
使用mapper代理的方法来开发dao时,程序员只需要干两件事即可:
1、编写mapper.xml映射文件 2、编写mapper接口(相当于dao接口)
Mapper接口开发需要遵循以下四个规范(建议初学者结合下图理解): 1、Mapper.xml文件中的namespace与mapper接口的类路径相同。 2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同 4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mapper接口开发四个规范属实有点费劲,我就在掘金上废了点劲找到了图,方便理解:**
原始Dao开发方式**
原始Dao开发方法需要程序员编写Dao接口和Dao实现类。
1. 编写映射文件**
编写映射文件如下:(也可以使用入门程序完成的映射文件)
<?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查询用户 -->
<select id="queryUserById" parameterType="int"
resultType="com.gx.mybatis.pojo.User">
select * from user where id = #{id}
</select>
<!-- 根据username模糊查询用户 -->
<select id="queryUserByUsername" parameterType="string"
resultType="com.gx.mybatis.pojo.User">
select * from user where username like '%${value}%'
</select>
<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.gx.mybatis.pojo.User">
<selectKey keyProperty="id" keyColumn="id" order="AFTER"
resultType="int">
SELECT 1576193
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
2. 编写Dao接口**
先进行DAO的接口开发,编码如下:
public interface UserDao {
/**
- 根据id查询用户
*
@param id
- @return
*/
User queryUserById(int id);
/**
- 根据用户名模糊查询用户
*
@param username
- @return
*/
List<User> queryUserByUsername(String username);
/**
- 保存用户
*
@param user
*/
void saveUser(User user);
}
3.编写Dao实现类**
编写的Dao实现类如下
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
super();
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User queryUserById(int id) {
// 创建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 执行查询逻辑
User user = sqlSession.selectOne("queryUserById", id);
// 释放资源
sqlSession.close();
return user;
}
@Override
public List<User> queryUserByUsername(String username) {
// 创建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 执行查询逻辑
List<User> list = sqlSession.selectList("queryUserByUsername", username);
// 释放资源
sqlSession.close();
return list;
}
@Override
public void saveUser(User user) {
// 创建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 执行保存逻辑
sqlSession.insert("saveUser", user);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
}
4.编写Dao测试**
创建一个JUnit的测试类,对UserDao进行测试(充当main方法),测试代码如下:
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
// 创建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SqlsessionFactory
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() {
// 创建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 执行查询
User user = userDao.queryUserById(1);
System.out.println(user);
}
@Test
public void testQueryUserByUsername() {
// 创建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 执行查询
List<User> list = userDao.queryUserByUsername("五");
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testSaveUser() {
// 创建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 创建保存对象
User user = new User();
user.setUsername("孙尚香肠");
user.setBirthday(new Date());
user.setSex("1");
user.setAddress("蜀国");
// 执行保存
userDao.saveUser(user);
System.out.println(user);
}
}
Mapper动态代理方式**
1.定义Mapper.xml(映射文件)**
定义mapper映射文件UserMapper.xml 将UserMapper.xml放在config下mapper目录下,效果如下:
2、编写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 -->
<!-- 还有一个很重要的作用,使用动态代理开发DAO,1. namespace必须和Mapper接口类路径一致 -->
<mapper namespace="com.gx.mybatis.mapper.UserMapper">
<!-- 根据用户id查询用户 -->
<!-- 2. id必须和Mapper接口方法名一致 -->
<!-- 3. parameterType必须和接口方法参数类型一致 -->
<!-- 4. resultType必须和接口方法返回值类型一致 -->
<select id="queryUserById" parameterType="int"
resultType="com.gx.mybatis.pojo.User">
select * from user where id = #{id}
</select>
<!-- 根据用户名查询用户 -->
<select id="queryUserByUsername" parameterType="string"
resultType="com.gx.mybatis.pojo.User">
select * from user where username like '%${value}%'
</select>
<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.gx.mybatis.pojo.User">
<selectKey keyProperty="id" keyColumn="id" order="AFTER"
resultType="int">
select 1576193
</selectKey>
insert into user(username,birthday,sex,address) values
(#{username},#{birthday},#{sex},#{address});
</insert>
</mapper>
3.编写UserMapper(接口文件)**
创建UserMapper接口代码如下:
public interface UserMapper {
/**
- 根据id查询
*
- @param id
- @return
*/
User queryUserById(int id);
/**
- 根据用户名查询用户
*
- @param username
- @return
*/
List<User> queryUserByUsername(String username);
/**
- 保存用户
*
- @param user
*/
void saveUser(User user);
}
4.加载UserMapper.xml文件**
修改SqlMapConfig.xml文件,添加以下所示的内容:
<!-- 加载映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml" />
<mapper resource="mapper/UserMapper.xml" />
</mappers>
5.编写测试**
编写的测试方法如下:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
// 创建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SqlsessionFactory
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() {
// 获取sqlSession,和spring整合后由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 从sqlSession中获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行查询方法
User user = userMapper.queryUserById(1);
System.out.println(user);
// 和spring整合后由spring管理
sqlSession.close();
}
@Test
public void testQueryUserByUsername() {
// 获取sqlSession,和spring整合后由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 从sqlSession中获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行查询方法
List<User> list = userMapper.queryUserByUsername("五");
for (User user : list) {
System.out.println(user);
}
// 和spring整合后由spring管理
sqlSession.close();
}
@Test
public void testSaveUser() {
// 获取sqlSession,和spring整合后由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 从sqlSession中获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 创建保存对象
User user = new User();
user.setUsername("刘备胎");
user.setBirthday(new Date());
user.setSex("2");
user.setAddress("鼠国");
// 执行查询方法
userMapper.saveUser(user);
System.out.println(user);
// 和spring整合后由spring管理
sqlSession.commit();
sqlSession.close();
}
}
总结**
优点**
使用数据访问对象的优点是应用程序的两个重要部分之间相对简单并严格分离,它们可以但不应该彼此了解任何东西,并且两者可预期的频繁和独立发展。改变业务逻辑可以依赖于相同的DAO接口,而对持久性逻辑的更改只要接口保持正确实现,就不会影响DAO客户端。存储的所有细节都隐藏在应用程序的其余部分中(见信息隐藏)。因此,可以修改一个DAO实现而不影响应用程序的其余部分,从而可能实现对持久性机制的更改。DAO充当了应用程序与数据库之间的中介,它们在对象与数据库记录之间来回转移数据。用测试替身取代DAO可以促进代码的单元测试,使测试不依赖于持久层。
在Java编程语言的非特定上下文中,数据访问对象作为一项设计概念可以用多种方式实现。这可以将应用程序中的数据访问部分分离为非常简单的接口,迁移到框架或商业产品。DAO编码范例可能需要一些技巧。像是Java Persistence API和Enterprise JavaBeans之类的技术已内置在应用程序服务器中,可以在JavaEE应用程序服务器的应用程序中使用。商业产品如TopLink可以在基于对象关系映射(ORM)的产品上使用。流行的开源ORM产品包括Doctrine (PHP))、Hibernate、iBATIS,以及JPA实现(例如Apache OpenJPA)。
缺点**
使用DAO的潜在缺点包括抽象泄漏、代码重复和抽象反演。尤其是将DAO作为常规Java对象的抽象会隐藏每个数据库访问的高成本,并且可能强迫开发人员触发多个数据库查询来检索普通SQL查询中一次就可取回的信息。如果一个应用程序需要多个DAO,人们可能发现自己对每个DAO重复基本上相同的创建、读取、更新和删除代码。不过,也可以实现一个处理常用操作的通用DAO来避免样板化代码。
以上是 【Java】DAO模式简介 的全部内容, 来源链接: utcz.com/a/98169.html