本文共 7619 字,大约阅读时间需要 25 分钟。
前言:在上一篇中,我们初识了Spring框架,了解到Spring其实是一个用于配置和管理bean的容器框架,在Web应用中纵跨了Web层,业务逻辑层和持久层。在本篇文章中我们就来看看Spring在数据访问层中起到的作用。本篇以实践为主,并不涉及到源码的深入分析。
在Web应用中,为了降低系统的耦合度,我们会将应用分层。数据访问层负责与数据库交互,执行与数据库CRUD有关的操作,ORM映射等操作。简而言之就是负责读写数据库的操作。
在学习Java的时候,我们有时候会看到JDBC的章节,里面阐述的基本上就是传统的数据库访问的实现,JDBC的在Web应用中的定位如图。具体的已经在中做了详细的讨论,已经了解的可以跳过。
分析:这种传统的使用JDBC API的数据访问层有什么不足呢?大体上可以归为(但不限于)以下四点:
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 RowMapperSpring配置文件中jdbcTemplate的相关配置,这里配置了DataSource数据源和JdbcTemplate示例。() { 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; }}
分析1:Spring配置文件中不需要配置service和dao,因为上面已经使用了Spring的自动注入功能。那么现在的做法和直接使用JDBC到底有什么不同呢?明显我们可以看到,使用JDBC的时候那些繁杂重复的步骤已经被JdbcTemplate封装起来了!(如驱动加载、获取Connection、Statement、关闭连接等)并且使用JDBC时会强制抛出的checked异常也被JdbcTemplate捕获消化掉了,转而抛出可以忽略的Runtime异常,现在程序员可以专注地处理业务逻辑!这就解决了上面的前三个问题了。但仍旧未能对SQL语句进行完全地抽象。JdbcTemplate实际上应用了Template Method模式。 分析2:Spring在这里起的作用/定位?
在Spring配置文件中配置EntityManagerFactoryorg.springframework spring-orm 4.3.3.RELEASE org.hibernate hibernate-core 5.1.0.Final org.hibernate hibernate-entitymanager 5.1.0.Final
使用注解标注业务模型类与数据库表的映射关系。
@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类上的注解正是我们的“指导”。关于这些注解的用法这里不详细说明,请自行查阅相关的资料。这就解决了上面存在的问题了。
业务层实现修改,第二个方法未做处理,同理变换。
@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分析1:可以看到TransactionTemplate类将事务处理的一些通用步骤封装了起来(事务划分,提交,异常回滚等),同样是采用了Template Method模式。这实际上也是体现了一个面向切面编程的思想,可以通过AOP声明式事务管理。 分析2:Spring在这里的作用/定位?() { public Student doInTransaction(TransactionStatus transactionStatus) { return studentDAO.getStudentInfosByNumber(number); } }); return student; } public boolean addStudent(Student student) { return studentDAO.insertStudent(student); }}