博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring学习笔记(二)---Spring对数据访问层的支持
阅读量:4094 次
发布时间:2019-05-25

本文共 7619 字,大约阅读时间需要 25 分钟。

前言:在上一篇中,我们初识了Spring框架,了解到Spring其实是一个用于配置和管理bean的容器框架,在Web应用中纵跨了Web层,业务逻辑层和持久层。在本篇文章中我们就来看看Spring在数据访问层中起到的作用。本篇以实践为主,并不涉及到源码的深入分析。

一、什么是数据访问层/持久层/DAO层?

在Web应用中,为了降低系统的耦合度,我们会将应用分层。数据访问层负责与数据库交互,执行与数据库CRUD有关的操作,ORM映射等操作。简而言之就是负责读写数据库的操作。

二、传统的数据访问层是如何实现的?

在学习Java的时候,我们有时候会看到JDBC的章节,里面阐述的基本上就是传统的数据库访问的实现,JDBC的在Web应用中的定位如图。具体的已经在中做了详细的讨论,已经了解的可以跳过。

分析:这种传统的使用JDBC API的数据访问层有什么不足呢?大体上可以归为(但不限于)以下四点:

  • 循环模式,也就是只要业务代码中有需要访问数据库的地方,我们都需要重复这一套步骤进行数据库的访问。这将造成维护的困难。
  • 数据库访问的代码和业务代码强耦合在一起,不利于维护。
  • 不适当的SQL异常处理操作。有时候程序员并不清楚如何处理SQL异常但又不能忽略(checked异常会强制抛给程序员处理)
  • 没有对SQL进行完全的抽象。也即是我们需要编写特定JDBC供应商的SQL语句,不利于应用的迁移。

三、Spring支持的JDBC

为了初步解决上面的问题,我们在项目中引入Spring框架,并且使用其对JDBC的支持和增强功能。下面直接从代码入手,讲解引入Spring之后的便利。(篇幅原因,数据库的创建请自行尝试。)
业务层接口:
public interface StudentService {    /**     * 通过学号查找学生信息     * @param number     * @return     */    public Student getStudentInfosByNumber(String number);    /**     * 添加学生     * @param student     * @return     */    public boolean addStudent(Student student);}
业务层实现,可以看到业务层这里是直接将工作交由给DAO层处理的。
@Service("studentService")public class StudentServiceImpl implements StudentService{    @Resource    private StudentDAO studentDAO;    public Student getStudentInfosByNumber(String number) {        return studentDAO.getStudentInfosByNumber(number);    }    public boolean addStudent(Student student) {        return studentDAO.insertStudent(student);    }}
DAO层接口:
public interface StudentDAO {    /**     * 通过学号查找学生信息     * @param number     * @return     */    public Student getStudentInfosByNumber(String number);    /**     * 添加学生     * @param student     * @return     */    public boolean insertStudent(Student student);}

DAO层实现,使用了Spring的JDBC支持,没有使用ORM框架,没有启动事务。底层仍旧是使用JDBC API。

@Repository("studentDAO")public class StudentDAOImpl implements StudentDAO{    @Resource    private JdbcTemplate jdbcTemplate;    public Student getStudentInfosByNumber(String number) {        if(number == null){            return null;        }        Student student=jdbcTemplate.queryForObject("select * from students where number=?", new RowMapper
() { public Student mapRow(ResultSet resultSet, int i) throws SQLException { Student student=new Student(); student.setId(resultSet.getInt("id")); student.setName(resultSet.getString("name")); student.setAge(resultSet.getInt("age")); if(resultSet.getString("gender").equals("MALE")) { student.setGender(GenderEnum.MALE); }else{ student.setGender(GenderEnum.FEMALE); } student.setNumber(resultSet.getString("number")); return student; } },number); return student; } public boolean insertStudent(Student student) { if(student == null){ return false; } if(student.getName() == null || student.getAge() == null || student.getGender() == null || student.getNumber() == null){ return false; } //性别:男-1,女-2 String gender="MALE"; if(student.getGender().getDesc().equals(Consts.FEMALE)){ gender="FEMALE"; } int count=jdbcTemplate.update("insert into students(name,age,gender,number) values(?,?,?,?) ", student.getName(),student.getAge(),gender,student.getNumber()); if(count > 0){ return true; } return false; }}
Spring配置文件中jdbcTemplate的相关配置,这里配置了DataSource数据源和JdbcTemplate示例。
分析1:Spring配置文件中不需要配置service和dao,因为上面已经使用了Spring的自动注入功能。那么现在的做法和直接使用JDBC到底有什么不同呢?明显我们可以看到,使用JDBC的时候那些繁杂重复的步骤已经被JdbcTemplate封装起来了!(如驱动加载、获取Connection、Statement、关闭连接等)并且使用JDBC时会强制抛出的checked异常也被JdbcTemplate捕获消化掉了,转而抛出可以忽略的Runtime异常,现在程序员可以专注地处理业务逻辑!这就解决了上面的前三个问题了。但仍旧未能对SQL语句进行完全地抽象。JdbcTemplate实际上应用了Template Method模式。
分析2:Spring在这里起的作用/定位?
  • Ioc控制反转:体现在依赖注入。
  • 提供了JdbcTemplate这个现成的组件,采用模板方法模式为我们抽象了JDBC。消除了冗余代码,解耦了数据访问操作和业务逻辑操作,处理了强制抛出的SQL异常。
分析3:这种方案存在的不足?
没有引入ORM框架,于是我们需要在每次数据访问操作的时候,需要手动进行对象关系映射,以便将数据库中的数据转换为业务模型(上面的Student的各种set操作)。这样系统同样会出现重复代码并且难以维护。于是下面的方向是引入ORM框架。

四、利用Spring继承Hibernate等ORM框架

在pom.xml中引入Hibernate依赖。
org.springframework
spring-orm
4.3.3.RELEASE
org.hibernate
hibernate-core
5.1.0.Final
org.hibernate
hibernate-entitymanager
5.1.0.Final
在Spring配置文件中配置EntityManagerFactory
使用注解标注业务模型类与数据库表的映射关系。
@Entity@Table(name = "students")public class Student {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private int id;    /**     * 姓名     */    private String name;    /**     * 学号     */    private String number;    /**     * 年龄     */    private Integer age;    /**     * 性别     */    @Enumerated(EnumType.STRING)    private GenderEnum gender;    //getter...  &&  setter...}
DAO层实现,使用了Hibernate作为ORM映射框架(底层封装了JDBC),未启用事务。
@Repository("studentDAO")public class StudentDAOImpl implements StudentDAO{    @Resource    private EntityManagerFactory entityManagerFactory;    public Student getStudentInfosByNumber(String number){        if(number == null)        {            return null;        }        EntityManager entityManager=entityManagerFactory.createEntityManager();        Student student=entityManager.find(Student.class,1);        entityManager.close();        return student;    }    public boolean insertStudent(Student student){        if(student == null){            return false;        }        if(student.getName() == null || student.getAge() == null || student.getGender() == null || student.getNumber() == null){            return false;        }        EntityManager entityManager=entityManagerFactory.createEntityManager();        EntityTransaction transaction=entityManager.getTransaction();        transaction.begin();        entityManager.persist(student);        transaction.commit();        entityManager.close();        return true;    }}
分析1:从上面DAO实现的代码来看,程序员已经不需要手动进行ORM映射操作了。这些工作交由Hibernate框架帮助我们完成,当然我们需要给它一些指导。那些Student类上的注解正是我们的“指导”。关于这些注解的用法这里不详细说明,请自行查阅相关的资料。这就解决了上面存在的问题了。
分析2:Spring在这里起的作用/定位?
  • Ioc控制反转:体现在依赖注入。
  • 提供了LocalContainerEntityManagerFactoryBean这个可复用的组件和提供了跟Hibernate框架集成的扩展点。

五、利用Spring启用事务

Spring的事务支持有声明式和编程式两种。这里采用编程式作示例。事务控制我们一般放在业务层进行。
首先在Spring的配置文件中添加如下配置:
业务层实现修改,第二个方法未做处理,同理变换。
@Service("studentService")public class StudentServiceImpl implements StudentService{    @Resource    private StudentDAO studentDAO;    @Resource    private TransactionTemplate transactionTemplate;    public Student getStudentInfosByNumber(final String number) {        Student student=transactionTemplate.execute(new TransactionCallback
() { public Student doInTransaction(TransactionStatus transactionStatus) { return studentDAO.getStudentInfosByNumber(number); } }); return student; } public boolean addStudent(Student student) { return studentDAO.insertStudent(student); }}
分析1:可以看到TransactionTemplate类将事务处理的一些通用步骤封装了起来(事务划分,提交,异常回滚等),同样是采用了Template Method模式。这实际上也是体现了一个面向切面编程的思想,可以通过AOP声明式事务管理。
分析2:Spring在这里的作用/定位?
  • Ioc控制反转:体现在依赖注入。
  • 提供了TransactionManager和TransactionTemplate这些现成的可复用的组件。

六、总结:

这篇文章实际上为了加深大家对于Spring的印象,所以我一直围绕Spring在各个阶段所起的作用来说清楚。因为代码中有一些比较私人的东西,所以就不放出来了,需要看的可以私戳我。

你可能感兴趣的文章
版本号比较(Compare two Version numbers)
查看>>
垃圾回收算法之分代回收算法
查看>>
垃圾回收算法之标记-清除算法
查看>>
垃圾回收算法之标记-整理算法
查看>>
垃圾回收算法之复制算法
查看>>
Java虚拟机工具之虚拟机进程状况工具jps
查看>>
Java虚拟机工具之虚拟机统计监控工具jstat
查看>>
sed编辑器基础之替换命令
查看>>
gawk程序基础
查看>>
JVM架构之JVM工作原理
查看>>
Java中的垃圾回收
查看>>
sed编辑器基础之替换命令(二)
查看>>
Java代码中如何交换两个对象
查看>>
Java中的随机数
查看>>
Java虚拟机工具之堆栈跟踪工具jstack定位死循环
查看>>
OpenCV在Microsoft Visual Studio 2010环境中的配置
查看>>
在VS(Visual Studio)中运行带有参数的控制台程序
查看>>
第N个偶斐波那契数
查看>>
字符数目相同的子字符串的数目
查看>>
Java虚拟机工具之堆栈跟踪工具jstack检测死锁
查看>>