Spring整合Hibernate实现Spring Data JPA

 

在上一篇文章《Spring整合Hibernate实现JPA持久化》中,我们已经介绍了怎样在Spring容器中合理地集成Hibernate来实现JPA的ORM机制。但是,细心的读者会发现,上一篇文章中使用了EntityManager来直接与数据库交互,存在这一定的耦合度,更重要的是每当新增或修改新的实体Entity的管理时,都需要重复的实现EntityManager在Dao层的逻辑,有些过于模版化,重复劳动的问题存在,所以这篇文章就来介绍下解决这个问题所采用的Spring Data来自动化JPA的实现,听着很牛是吧!

 

 

l  基本储备技术介绍

l  Spring Data如何实现JPA

l  Spring Data JPA重构实例

 

 

一、基础储备技术介绍

在介绍如何实现之前,很有必要先介绍些储备内容。

首先,我们需要在Maven中引入spring-data-jpa软件包,参看如下内容:

    <dependency> 

        <groupId>org.springframework.data</groupId> 

        <artifactId>spring-data-jpa</artifactId> 

        <version>1.10.1.RELEASE</version> 

   </dependency>

这里使用了1.10.1.RELEASE版本,注意不同版本与Spring和Hibernate版本的兼容,读者只需要按照上篇文章及这里的介绍实现即可。在这个软件包里,提供了JpaRepository接口(继承自Repository),它的作用就是留给开发者自定义Dao层接口时进行拓展继承的,其用来绑定@Entity模型以及模型对应的ID类型的,这样绑定声明后,在程序或容器启动时进行初始化时,Spring Data根据@Entity注解找到该实体,并通过反射和注解技术来获取实体类的类名字、属性以及方法等参数,然后通过其默认提供的18个方法API(这里不做介绍,读者可执行查阅资料了解),来实现对实体的管理及持久化的封装。

 

其次,Spring Data默认生成的实体持久化方法遵循着一种模式,有助于生成针对数据库的查询,比如:

findUserByFirstnameAndLastnameOrderByLastname()

针对这个方法,SpringData是这样的规定的:find/get/read是查询动词,不论使用哪个,功能都是一样的;User代表主题,指的是被定义为@Entity实体的类名;By仅仅是一个连接符而已,其主要是用来连接查询条件(断言),以及断言内查询条件的连接,比如:OrderByLastname;FirstnameAndLastnameOrderByLastname则比较明显,是查询SQL的条件断言,这里指的根据User的Firstname和Lastname查询记录,并根据Lastname生序排列结果,另外,还可以忽略断言条件的大小写,以及判断过滤等,具体里面的内容较多,建议读者查阅相关资料熟悉了解。

 

最后,我们来简单介绍下DSL(领域驱动语言):其思想主要是“求专不求全”,不像其它通用语言那样涵盖一切范围的问题,而是专门针对某一特定问题的计算机语言。在本质上,Spring Data中已经定义了一组小规模的领域驱动语言,并且持久化的细节支持,都是通过Repository方法的签名来描述的。但是,DSL也有其局限性,在其所提供的实体查询方法无法满足需求时,我们就得借助于@Query等来实现更为复杂的需求,具体会在下面实现部分介绍。

 

 

二、Spring Data如何实现JPA

1、定义查询方法,JpaRepository提供的CURD方法

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

      // 这里不需要任何方法

}

 

使用JpaRepository绑定User实体(@Entity标注的实体类),并指定主键(ID)类型为String类型。此接口体内,不需要声明任何方法,直接使用由Spring Data默认提供的18个方法。

 

2、定义查询方法,JpaRepository规则支持的方法

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

    // 定义查询方法,供JpaRepository默认18个方法无法满足时

    User findByUsername(String userName);

    // 定对查询方法,根据emailmobile查询多条记录

    List<User>  findByEmailOrMobile(String email,String mobile);

}

 

这里的两个方法,虽然是开发者自行定义的,但是它们满足Spring Data的规则,所以开发者不需要具体实现这些方法,所有的工作都交给SpringData自动完成。

 

3、声明自定义查询方法,采用@Query封装SQL操作

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> {

    // 声明自定义查询,根据email模糊查询多条记录

    @Query("select u from User u where u.email like '[email protected]%'")

    List<User> findAllEmailUsers();

}

 

需要特殊说明的是,在SpringData默认的DSL不能满足需求时,那么就需要结合其支持的拓展注解@Query来灵活自定义更为复杂的SQL操作,当然这里只是为了演示用法,仅仅是实现了模糊匹配的查询。另外,findAllEmailUsers方法是用户自定义的,此时可以不需要符合Spring Data的规则,可随意命名。

 

4、混合自定义操作方法,与降级EntityManager结合

UserRepository.java:

public interface UserRepository extends JpaRepository<User,String> , UserExtRepository {

     // 这里不需要任何方法

}

 

这里主要是在SpringData默认方法,以及@Query都不能满足需求时(虽然很少),就需要降级使用老版的EntityManager来实现了,当然,虽然结合使用了EntityManager,也就需要实现对应的接口,这里新建立了一个UserExtRepository接口,具体内容如下:

public interface UserExtRepository {

   // 单一的Spring Data JPA无法完成的操作,

   // 需要降低版本,结合EntityManager协同完成

   public int batchUpdate(String email);

}

 

那么,接下来就得提供一个它的实现类:UserRepositoryImpl了,内容如下:

@Repository("UserRepositoryImpl")

public class UserRepositoryImpl implements UserExtRepository {

    @PersistenceContext

    private EntityManager em;

   

    @Override

    public intbatchUpdate(String email) {

       String update = "update User u set u.status=1 where u.email='"+email+"'";

       return em.createQuery(update).executeUpdate();

    }

   

}

 

需要注意的是,SpringData在结合EntityManager实现JPA时,需要嵌入了EntityManager的实现类,如:UserRepositoryImpl,需要满足Spring Data的命名规则,其默认必须是采用xxxImpl的形式才能被识别,如果需要定义为别的格式命名,只需要在@EnableJpaRepositories开支持时(在Spring上下文配置中声明,这里使用的是JavaConfig方式),指定命名格式即可,具体如下实现:

@EnableJpaRepositories(basePackages="com.cwteam.orm.dao",repositoryImplementationPostfix="Helper")

同时,还有个需要注意的是,既然你想在Spring Data中混合使用EntityManager的功能,那么你的实现类的命名需要有一定规则,如:UserRepositoryImpl,其与我们继承JpaRepository的自定义类UserRepository相对应吧!另外,这里的basePackages的作用比较显示,用来指定扫描初始化Entity的位置。

 

 

三、Spring Data JPA重构实例

在完成了上面所有工作之后,接下来具体介绍下针对上一篇文章的例子重构,实际上改动量较小,主要修改Dao相关,以及控制器调用相关,具体各个新增或修改的内容已经在上面第二部分有介绍,这里直接罗列代码!

 

1、Dao层相关

新增UserRepository.java:

public interface UserRepository extends JpaRepository<User,String>,UserExtRepository {

   

    // 定义查询方法,供JpaRepository默认18个方法无法满足时

    User findByUsername(String userName);

   

    // 定对查询方法,根据emailmobile查询多条记录

    List<User> findByEmailOrMobile(String email,String mobile);

   

    // 声明自定义查询,根据email模糊查询多条记录

    @Query("select u from User u where u.email like '[email protected]%'")

    List<User> findAllEmailUsers();

   

}

 

新增UserExtRepository.java:

public interface UserExtRepository {

    // 单一的Spring Data JPA无法完成的操作,

    // 需要降低版本,结合EntityManager协同完成

    public int batchUpdate(String email);

}

 

新增UserRepositoryImpl.java:

@Repository("UserRepositoryImpl")

public class UserRepositoryImpl implements UserExtRepository {

    @PersistenceContext

    private EntityManager em;

   

    @Override

    public int batchUpdate(String email) {

       String update = "update User u set u.status=1 where u.email='"+email+"'";

       return em.createQuery(update).executeUpdate();

    }

   

}

 

 

2、Service层

新增UserRepositoryService.java:

@Service("UserRepositoryService")

public class UserRepositoryService {

    @Autowired

    private UserRepository userRepository;

   

    // 新增记录

    @Transactional

    public void saveUser(User user) {

       userRepository.save(user);

    }

   

    // 更新记录

    @Transactional

    public void updateUser(User user) {

       User result = findUserByID(user);

       result.setUsername(user.getUsername());

       result.setPassword(user.getPassword());

       result.setEmail(user.getEmail());

       result.setMobile(user.getMobile());

    }

   

    // 根据ID查询记录

    public User findUserByID(User user) {

       return userRepository.findOne(user.getId());

    }

   

    // 根据用户名查询

    public User findByUsername(String username) {

       return userRepository.findByUsername(username);

    }

   

    // 根据邮箱或手机查询

    public List<User>  findByEmailOrMobile(String email,String mobile) {

       return userRepository.findByEmailOrMobile(email, mobile);

    }

   

    // 根据email模糊匹配

    public List<User>  findAllEmailUsers() {

       return userRepository.findAllEmailUsers();

    }

   

    // 查询所有记录

    public List<User>  findUsersNoPage() {

       return userRepository.findAll();

    }

   

    // 根据用户名删除记录

    @Transactional

    public void delete(User user) {

       userRepository.delete(user);

    }

   

    // 批量修改status状态

    @Transactional

    public int batchUpdate(String email) {

       return userRepository.batchUpdate(email);

    }

}

 

3、Controller层

UserController.java:

@Controller

@RequestMapping("/user")

public class UserController {

    @Autowired

    UserRepositoryService userRepositoryService;

   

    // 操作页面

    @RequestMapping(value="/show",method=RequestMethod.GET)

    public ModelAndView show() throws Exception {

       // 业务逻辑处理

       List<User> result = userRepositoryService.findUsersNoPage();

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", result);

       return mav;

    }

   

    // 新增记录

    @RequestMapping(value="/save",method=RequestMethod.POST)

    public ModelAndView save(HttpServletRequest request) throws Exception {

       User user = new User();

       user.setUsername(request.getParameter("username"));

       user.setPassword(request.getParameter("password"));

       user.setMobile(request.getParameter("mobile"));

       user.setEmail(request.getParameter("email"));

       // 业务逻辑处理

       userRepositoryService.saveUser(user);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", userRepositoryService.findUsersNoPage());

       return mav;

    }

   

    // 更新记录

    @RequestMapping(value="/update",method=RequestMethod.POST)

    public ModelAndView update(HttpServletRequest request) throws Exception {

       User user = new User();

       user.setId(request.getParameter("id"));

       user.setUsername(request.getParameter("username"));

       user.setPassword(request.getParameter("password"));

       user.setMobile(request.getParameter("mobile"));

       user.setEmail(request.getParameter("email"));

       // 业务逻辑处理

       userRepositoryService.updateUser(user);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", userRepositoryService.findUsersNoPage());

       return mav;

    }

   

    // 根据ID查询

    @RequestMapping(value="/find",method=RequestMethod.GET)

    public ModelAndView find(HttpServletRequest request) throws Exception {

       User user = new User();

       user.setId(request.getParameter("id"));

       // 业务逻辑处理

       User user2 = userRepositoryService.findUserByID(user);

       List<User> result = new ArrayList<User>();

       result.add(user2);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", result);

       return mav;

    }

   

    // 根据用户名查询

    @RequestMapping(value="/findbyusername",method=RequestMethod.GET)

    public ModelAndView findByUsername(HttpServletRequest request) throws Exception {

       User user = new User();

       user.setUsername(request.getParameter("username"));

       // 业务逻辑处理

       User userTmp = userRepositoryService.findByUsername(user.getUsername());

       List<User> result = new ArrayList<User>();

       result.add(userTmp);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", result);

       return mav;

    }

   

    // 根据emailmobile查询

    @RequestMapping(value="/findbyemailormobile",method=RequestMethod.GET)

    public ModelAndView findByEmailOrMobile(HttpServletRequest request) throws Exception {

        User user = new User();

       user.setEmail(request.getParameter("email"));

       user.setMobile(request.getParameter("mobile"));

       // 业务逻辑处理

       List<User> result = userRepositoryService.findByEmailOrMobile(user.getEmail(), user.getMobile());

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", result);

       return mav;

    }

   

    // 根据email模糊匹配查询

    @RequestMapping(value="/findallemailusers",method=RequestMethod.GET)

    public ModelAndView findAllEmailUsers(HttpServletRequest request) throws Exception {

       // 业务逻辑处理

       List<User> result = userRepositoryService.findAllEmailUsers();

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", result);

       return mav;

    }

   

    // 根据ID删除记录

    @RequestMapping(value="/delete",method=RequestMethod.GET)

    public ModelAndView delete(HttpServletRequest request) throws Exception {

       User user = new User();

       user.setId(request.getParameter("id"));

       // 业务逻辑处理

       userRepositoryService.delete(user);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", userRepositoryService.findUsersNoPage());

       return mav;

    }

   

    // 根据email匹配批量修改

    @RequestMapping(value="/batchupdate",method=RequestMethod.POST)

    public ModelAndView extUpdate(HttpServletRequest request) throws Exception {

       String email = request.getParameter("email");

       // 业务逻辑处理

       userRepositoryService.batchUpdate(email);

       // 页面数据渲染

       ModelAndView mav = new ModelAndView("test/user_operation");

       mav.addObject("result", userRepositoryService.findUsersNoPage());

       return mav;

    }

   

}

 

  

4、View页面

具体的页面部分,请读者结合上一篇的jsp页面进行修改实现,具体实现的效果会在下面逻辑,读者也可以自行设计布局。 

整体效果如下:

Spring整合Hibernate实现Spring Data JPA

 

A、新增一条记录

Spring整合Hibernate实现Spring Data JPA

 

新增cwteam7记录

 

B、修改一条记录

Spring整合Hibernate实现Spring Data JPA

修改cwteam7的mobile值

 

C、根据ID查询

Spring整合Hibernate实现Spring Data JPA

 

根据ID查询cwteam7记录

 

D、删除一条记录

Spring整合Hibernate实现Spring Data JPA

删除cwteam7这条记录

 

E、根据用户名查询

Spring整合Hibernate实现Spring Data JPA

根据用户名查询cwteam5记录

 

F、根据email或mobile查询

Spring整合Hibernate实现Spring Data JPA

 

 

G、根据email模糊查询

Spring整合Hibernate实现Spring Data JPA

查询条件,请查看UserRepository代码部分。

 

H、匹配email批量修改

Spring整合Hibernate实现Spring Data JPA

 

修改的细节,请查看UserRepositoryImpl代码部分。

 

 

 

 

 

 

 

好了,由于作者水平有限,如有不正确或是误导的地方,请不吝指出讨论(技术交流群:497552060(新))

 

 

相关文章: