SpringBoot 整合 Hibernate
文章目录
最近和同事一起开发一个 SpringBoot + Hibernate + Vue 的进销存管理系统(https://github.com/pss-dev/pss) 遇到了一个问题, 项目基础架构使用 SpringBoot 开发, 但是 ORM 框架在讨论后选择了
Hibernate
而不是Mybatis
, 所以帅帅我本着认真负责的态度就先网上看了看 SpringBoot 与 Hibernate 的整合, 最后发现网上所谓的SpringBoot 整合 Hibernate
都是打着Hibernate
的幌子, 最后整合的是Hibernate-Jpa
, 底层写的都是jpa
的代码, 然后帅帅就又开始自己研究了, 于是乎也就有个这篇博文和大家分享.
PS: 还记得学习 Java SE 到 GUI 部分的时候老师说这玩意没人用, 我们不讲, emmm… 奈何这周工作中需要维护一个老旧项目, 帅帅我的心情十分沉重, 在
Java GUI
里面遨游的时候, 那个心情…此处省略一万字…, But, But, But, 到最后发现, 我 TM 竟然觉得Java GUI
还挺有意思的.所以本来打算出一篇
Java GUI
的文章, 然后给我们的项目Angboot: https://github.com/DreamLi1314/angboot 添加 GUI 支持.奈何这周比较忙(谋划一件大事, 后边再告诉你哦), 所以下篇
分享一些 Java GUI 的东东
, 虽然比较基础和老旧, 但是帅帅觉得其实还是有点用的, 可以自己玩玩做一些东西, 比如帅帅大学学 Oracle 的时候觉得 Oracle 的客户端 PL/SQL 特别慢, 卡(电脑不行, 软件功能多), 因此就用Java GUI
自己写一了一个Oracle Client
, 比较简洁和快速, 满足开始学习时 SQL 的执行和 Result 的显示.
PS: 听过一个程序员对疫情居家隔离的总结 ----- 疫情居家隔离的开始, 也是你进入 BAT 最好的时机.
另外还有朋友对我说过一句话 ----- 虽然受疫情影响, 大部分公司都不涨工资了, 但是你可以换一个工资高的公司啊
帅帅我是很惭愧的, 所以最近开始认真着手准备一个系列博文 ---- <<面向面试官编程>>系列, 一方面对自己知识的梳理, 另一方变也希望对大家面试, 跳槽有所帮助.(如果帮到您, 记得回来请我喝咖啡哦…哈哈…)
好了, 废话少说, 开始本文的重点:
SpringBoot 整合 Hibernate
(扯了半天皮, 文章主题没忘吧? 罒ω罒)
1. 配置 SessionFactory
package com.pssdev.pss.config;
@Configuration
// 读取 application.yml/properties 文件中以 `pss.hibernate` 开头的配置并映射到 class 属性
@ConfigurationProperties(prefix = "pss.hibernate")
public class SessionFactoryConfig {
// 注入数据源, 帅帅使用的 Druid
@Autowired
public SessionFactoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean("sessionFactory")
public LocalSessionFactoryBean getSessionFactory() throws IOException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource); // 配置数据源
// 如果使用 xml 配置则使用该方法进行包扫描
// PathMatchingResourcePatternResolver pmprpr = new PathMatchingResourcePatternResolver();
// Resource[] resource = pmprpr.getResources("classpath*:com/pssdev/pss/**/domain/*.hbm.xml");
// localSessionFactoryBean.setMappingLocations(resource);
// 现在配置基本都切换到 java config, 帅帅也是, 所以使用 AnnotatedPackages
localSessionFactoryBean.setAnnotatedPackages("classpath*:com/pssdev/pss/entity");
// 添加 Hibernate 配置规则
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect",dialect);
hibernateProperties.put("current_session_context_class", sessionContextClass);
hibernateProperties.put("hibernate.show_sql", showSql);
hibernateProperties.put("hibernate.format_sql", formatSql);
hibernateProperties.put("hibernate.hbm2ddl.auto", ddlAuto);
localSessionFactoryBean.setHibernateProperties(hibernateProperties);
localSessionFactoryBean.setPackagesToScan("com.pssdev.pss.entity");
return localSessionFactoryBean;
}
...getter and setter
private String dialect;
private String sessionContextClass;
private boolean showSql;
private boolean formatSql;
private String ddlAuto;
private final DataSource dataSource;
}
- application.yml
# Hibernate config
pss:
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.H2Dialect
session-context-class: org.springframework.orm.hibernate5.SpringSessionContext
formatSql: true
show-sql: true
2. 配置事务 TransactionConfig
package com.pssdev.pss.config;
@Configuration
public class TransactionConfig implements TransactionManagementConfigurer {
@Autowired
public TransactionConfig(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// 事务管理交给 HibernateTransactionManager
@Bean("transactionManager")
public HibernateTransactionManager getTransactionManager(){
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();
hibernateTransactionManager.setSessionFactory(sessionFactory);
return hibernateTransactionManager;
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return getTransactionManager();
}
private final SessionFactory sessionFactory;
}
3. 排除 Jpa 自动配置
package com.pssdev.pss;
// 排除 DataSource, jpa, HibernateJpa 的自动配置
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
// 开启事务管理
@EnableTransactionManagement(proxyTargetClass = true)
public class PssApplication {
public static void main(String[] args) {
SpringApplication.run(PssApplication.class, args);
}
}
4. 应用
4.1 添加 department entity 完成一对多
映射
package com.pssdev.pss.entity;
@Entity(name = "t_dept")
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column
private String initials;
// 多对一映射
@ManyToOne
@JoinColumn(name = "parent_id")
private Department parent;
// 一对多映射
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Department> children = new HashSet<>();
public Department() {
}
public Department(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInitials() {
return initials;
}
public void setInitials(String initials) {
this.initials = initials;
}
public Set<Department> getChildren() {
return children;
}
public void setChildren(Set<Department> children) {
this.children = children;
}
public Department getParent() {
return parent;
}
public void setParent(Department parent) {
this.parent = parent;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
", initials='" + initials + '\'' +
", parent=" + (parent != null ? parent.id : null) +
", children=[" + (children != null
? children.stream().map(d -> d.id + "").collect(Collectors.joining(","))
: null)
+ "]}";
}
}
4.2 DepartmentService
package com.pssdev.pss.service.impl;
@Service("departmentService")
public class DepartmentServiceImpl implements DepartmentService {
@Autowired
public DepartmentServiceImpl(DepartmentDao departmentDao) {
this.departmentDao = departmentDao;
}
@Transactional(readOnly = true)
@Override
public Department getDepartment(Integer parentId) {
return departmentDao.getDepartment(parentId);
}
@Transactional(readOnly = true)
@Override
public List<Department> getDepartments(Integer parentId) {
return parentId == null
? departmentDao.getAllDepartments()
: Arrays.asList(departmentDao.getDepartment(parentId));
}
@Transactional(readOnly = true)
@Override
public List<Department> getDepartments() {
return departmentDao.getAllDepartments();
}
@Transactional
@Override
public int insertDepartment(Department department) {
return departmentDao.insertDepartment(department);
}
@Transactional
@Override
public void updateDepartment(Department department) {
departmentDao.updateDepartment(department);
}
@Transactional
@Override
public void deleteDepartment(Integer id) {
departmentDao.deleteDepartment(id);
}
private final DepartmentDao departmentDao;
}
对于
Get
操作我们事务管理标注readOnly=true
4.3 DepartmentDao
package com.pssdev.pss.dao.impl;
@Repository("departmentDao")
public class DepartmentDaoImpl extends BaseDao implements DepartmentDao {
@Override
public List<Department> getAllDepartments() {
Session session = getSession();
CriteriaQuery<Department> query = session.getCriteriaBuilder()
.createQuery(Department.class);
query.from(Department.class);
return session.createQuery(query).list();
}
@Override
public Integer insertDepartment(Department department) {
Session session = getSession();
return (Integer) session.save(department); // 用 save
}
@Override
public Department getDepartment(Integer parentId) {
return getSession().find(Department.class, parentId); // 用 find 而不是 load
}
@Override
public void deleteDepartment(Integer id) {
Department department = getDepartment(id);
if(department != null) {
getSession().delete(department); // 用 delete, 而不是 remove
}
}
@Override
public void updateDepartment(Department department) {
getSession().update(department);
}
}
5. 测试
测试框架用的是 Junit5 —
jupiter
测试框架
package com.pssdev.pss.service;
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DepartmentServiceTests {
@Autowired
private DepartmentService departmentService;
@Test
@Order(1)
public void testNonNull() {
Assertions.assertNotNull(departmentService, "Init Department Dao Error.");
}
@Order(2)
@RepeatedTest(value = 5, name = "Insert 5 dept to make sure get dept id not null")
public void testInsertTopDepartment() {
int index = (int) (1000 * Math.random());
Department dept1 = new Department();
dept1.setName("Top Level" + index);
dept1.setInitials("TL" + index);
departmentService.insertDepartment(dept1);
}
@Test
@Order(3)
public void testGetDepartments() {
List<Department> departments = departmentService.getDepartments();
Assertions.assertNotNull(departments, "Departments is empty");
}
@ParameterizedTest
@ValueSource(ints = {2})
@Order(4)
public void testGetDepartment(int id) {
Department department = departmentService.getDepartment(id);
Assertions.assertNotNull(department, "Department is null");
Assertions.assertEquals(department.getId(), id, "Get department error.");
LOGGER.info("Department: {}", department);
}
@ParameterizedTest
@ValueSource(ints = {2})
@Order(5)
public void testInsertChildDepartment(int id) {
Department parentDept = departmentService.getDepartment(id);
LOGGER.info("Query parent dept first: {}", parentDept);
Department cDept1 = new Department();
cDept1.setName("Child Level1");
cDept1.setInitials("CL1");
cDept1.setParent(parentDept);
Department cDept2 = new Department();
cDept2.setName("Child Level2");
cDept2.setInitials("CL2");
cDept2.setParent(parentDept);
// insert children
int cid1 = departmentService.insertDepartment(cDept1);
int cid2 = departmentService.insertDepartment(cDept2);
LOGGER.info("Insert dept {}, {}.", cid1, cid2);
// get parent again
parentDept = departmentService.getDepartment(id);
LOGGER.info("Query parent dept again: {}", parentDept);
Set<Department> children = parentDept.getChildren();
Assertions.assertFalse(
children == null || children.size() < 1, "Query children error.");
Set<Integer> childIds = children.stream().map(d -> d.getId()).collect(Collectors.toSet());
Assertions.assertTrue(childIds.contains(cid1), "Missing child 1");
Assertions.assertTrue(childIds.contains(cid2), "Missing child 2");
}
@ParameterizedTest
@ValueSource(ints = 2)
@Order(6)
public void testDeleteDepartment(int id) {
departmentService.deleteDepartment(id);
}
private static final Logger LOGGER
= LoggerFactory.getLogger(DepartmentServiceTests.class);
}