1. 数据访问层 (DAO模式)
通常我们的应用都要使用数据,涉及到大量的数据存取, 对于企业级应用,数据存放在关系型数据库是最常用的方案。前文提到Spring MVC将前端展现与业务逻辑分离,为了让程序结构更清晰,我们还会将数据访问从业务逻辑中也分离出来,做为一个数据访问层(持久化数据层)。这样我们就获得了一个J2EE经典的三层架构: 表现层 - 业务逻辑层 - 数据访问层。
数据访问层通常使用的DAO模式进行封装,DAO模式主要包括三部分:
- DAO接口: 对需要的数据库操作的接口,提供外部(业务逻辑层)使用。这样业务逻辑层与具体的数据访问分离开来。
- DAO 实现类: 针对不同数据库编写DAO接口的具体实现。
- 实体类: 用于在应用中存放与传递对象数据。类似于MVC中的Model。
我们再看上文的案例数据库,假设一个业务逻辑,需要查询指定用户的账户余额。那我们对数据库访问层的DAO设计如下:
首先我们定义实体类, 我们通常不会直接将JDBC的ResultSet返回给业务层,而是将数据抽象成对象,这里我们需要设计一个Account的实体类,实体类是没有业务逻辑的普通类,只有字段和对应的getter/setter方法。可以理解成C语言中的结构体。
package org.littlestar.learning.package8.entity;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = -242844598250198468L;
private long id;
private String name;
private double balance;
public Account() {}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
接着我们将数据访问抽象成接口,定义DAO接口:
package org.littlestar.learning.package8;
import org.littlestar.learning.package8.entity.Account;
public interface AccountDao {
/**
* 获取指定账户编号获取账户信息。
* @param id 账户编号
* @return 指定的账户信息,如果没有找到返回null。
*/
Account findAccountById(long id);
}
编写DAO接口的实现类,我们使用上文的JdbcTemplate来实现与数据库交互。
package org.littlestar.learning.package8;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public Account findAccountById(long id) {
String sqlText = "select * from learning.account where acct_id = ?";
RowMapper<Account> rowMapper = new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getLong("acct_id"));
account.setName(rs.getString("name"));
account.setBalance(rs.getDouble("balance"));
return account;
}
};
List<Account> accounts = jdbcTemplate.query(sqlText, rowMapper, id);
return accounts.isEmpty() ? null : accounts.get(0);
}
}
这样数据存储的访问层已经编写完成,在业务逻辑中就可以直接使用了:
...
@Autowired
AccountDao accountDao;
@Test
public void showAccountBalance() {
int id = 2;
Account account = accountDao.findAccountById(id);
System.out.println("Hello " + account.getName() + ", your balance is: " + account.getBalance());
}
...
DAO分离后,业务逻辑简单清晰了很多,没有了Java.sql.*包下了Connection, Statement, ResultSet这些JDBC接口/实现类,实现了业务逻辑与数据库访问的解耦。
2. ORM/JPA
上文AccountDaoImpl中,我们通过Spring-jdbc的RowMapper,将数据库返回的结果集(游标)映射成Java对象。手动编写比较繁琐,有没有方法自动将数据库对象映射成java的对象呢?有的,那就是使用ORM (Object Relational Mapping)框架。Java平台下,当前主流的有: Hibernate和MyBatis。相对于JdbcTemplate,ORM持久化框架对JDBC进行了更高级的封装(隐藏了更多的数据库相关细节),让持久层开发更符合面向对象。开发者通常不需要编写SQL语句,不需要关注数据库是Oracle还是MySQL,底层的SQL语句由框架自动生成。
在学习具体的持久化框架之前,我们先了解一下JPA(Java Persistence API)。JPA是Java对象持久化的API, 是J2EE 5.0的标准规范的一部分,JPA的目的是统一Java应用程序的持久层编程模型。JPA定义了一系列的接口和注解(在javax.persistence.*包下)和一些配置规范(配置文件定义)。JPA没有具体的实现,JPA必须和实现了JPA的框架一起使用。类似于JDBC,如果没有JDBC驱动,光有JDBC的API是无法访问数据库的。
Hibernate是JPA的主要实现之一, Hibernate从3.2开始兼容JPA。你只需要JPA配置文件中provider指定Hibernate,就可以使用JPA实现数据持久化访问。你也可以在Hibernate中使用JPA定义的注解(如@Entity, @Column, @Id, …)来定义对象映射。
在JPA规范中,配置xml配置文件名为persistence.xml,必须放在/META-INF目录下。
<persistence
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="jpa-with-hibernate">
<description>JPA with Hibernate Example</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>...</class>
...
</persistence-unit>
</persistence>
JPA主要使用EntityManager定义的方法实现数据持久化操作:
...
EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa-with-hibernate");
EntityManager entityManager = factory .createEntityManager();
entityManager.XXX();
...
3. Hibernate
Hibernate是一个基于JDBC的开源的持久化框架,遵循LGPL V2.1开源许可协议, 是一个优秀的全自动ORM框架。在SpringBoot中,JPA的默认实现使用的就是Hibernate。下面学习Spring中使用Hibernate。在开始前需要先引入相关的依赖:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.5.Final</version>
</dependency>
<!-- spring-orm包含了Spring封装的一些Hiberate工具类, 如HibernateTemplate -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.15</version>
</dependency>
3.1. Spring使用xml配置文件集成Hibernate
配置Hibernate相关bean,并装载入Spring容器:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config />
<context:component-scan base-package="org.littlestar.learning.package8" />
<!-- Hibernate 依赖JDBC -->
<context:property-placeholder location="classpath:org/littlestar/learning/package8/datasource.properties" />
<bean id="hikariDataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="${spring.jdbc.driverClassName}" />
<property name="jdbcUrl" value="${spring.jdbc.url}" />
<property name="username" value="${spring.jdbc.username}" />
<property name="password" value="${spring.jdbc.password}" />
<property name="connectionTestQuery" value="${spring.jdbc.connectionTestQuery}" />
<property name="maximumPoolSize" value="${spring.jdbc.maxPoolSize}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="hikariDataSource" />
<property name="packagesToScan" value="org.littlestar.learning.package8.entity" />
<property name="mappingLocations">
<list> <!-- Hibernate O/R对象关系映射配置文件位置 -->
<value>classpath:org/littlestar/learning/package8/account.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop> <!-- MySQL数据库方言 -->
<prop key="hibernate.show_sql">true</prop> <!-- 日志输出显示sql -->
<prop key="hibernate.format_sql">true</prop> <!-- 日志输出中格式化sql -->
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.autoReconnect">true</prop>
</props>
</property>
</bean>
<!-- bean class="org.littlestar.learning.package8.AccountDaoImpl" /-->
<!-- HibernateTemplate -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置事务管器,管理持久化过程中的事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 将@Transactional注解的方法自动加入事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
接下来编写Hibernate的O/R对象关系配置文件: account.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping schema="learning">
<class name="org.littlestar.learning.package8.entity.Account" table="account">
<id name="id" column="acct_id" type="java.lang.Long">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String" />
<property name="balance" column="balance" />
</class>
</hibernate-mapping>
配置完成后,我们就可以使用Hibernate实现DAO。我们需要的是org.hibernate.Session, Session由SessionFactory创建,SessionFactory由Spring容器做为一个bean装载(通过org.springframework.orm.hibernate5.LocalSessionFactoryBean), 我们只需要@Autowired SessionFactory就可以获取其实例。然后通过SessionFactory的openSession或者getCurrentSession方法即可拿到session。两种方法获取方式有不同:
- openSession :每次都会打开一个新的session,用完之后需手动关闭session。
- getCurrentSession:重用同一个session,不需要手动关闭,由Hibernate管理线程安全和事务,性能上更优,但使用上也有限制。
获得Session实例后,就可以调用session的方法实现我们需要的持久化操作,Hibernate支持一种类似于SQL的查询语句, HQL(Hibernate Query Language),HQL是面向对象(实体类)的,而SQL是针对数据库表的。Hibernate也支持使用原生SQL语句,因为SQL语句是硬编码,不如HQL兼容性好(可兼容不同的数据库)。
类似于JdbcTemplate , Spring也提供了封装Hibernate的API的工具类HibernateTemplate,我们也可以直接使用HibernateTemplate简化编码。
package org.littlestar.learning.package8;
import java.util.List;
import javax.persistence.TypedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.littlestar.learning.package8.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Component // 必须是一个Bean, 才能让Spring容器自动装载(@Autowired) SessionFactory
public class AccountDaoImpl implements AccountDao {
@Autowired
private SessionFactory sessionFactory;
//HibernateTemplate: Helper class that simplifies Hibernate data access code.
@Autowired
private HibernateTemplate hibernateTemplate;
/*
获取所有Account数据, 对应SQL语句:
select
account0_.acct_id as acct_id1_0_,
account0_.name as name2_0_,
account0_.balance as balance3_0_
from
account account0_
*/
@Override
@Transactional( // findAll()方法的事务相关设置
value = "transactionManager",
rollbackFor = Exception.class,
isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRED,
readOnly = true)
public List<Account> findAll() {
/*
// 使用HQL实现.
String hql = "from Account"; // 在HQL中使用的是对象名, 必须与实体类的类名一致, 且严格区分大小写。
Session session = sessionFactory.getCurrentSession();
@SuppressWarnings("unchecked")
TypedQuery<Account> query = session.createQuery(hql);
List<Account> accounts = query.getResultList();
return accounts;
*/
// 使用HibernateTemplate实现
return hibernateTemplate.loadAll(Account.class);
}
/*
根据主键查询, 对应SQL语句:
select
account0_.acct_id as acct_id1_0_,
account0_.name as name2_0_,
account0_.balance as balance3_0_
from
account account0_
where
account0_.acct_id=?
*/
@Override
@Transactional(readOnly = true)
public Account findAccountById(long id) {
// 使用HQL实现
Session session = sessionFactory.getCurrentSession();
String hql = "from Account as a WHERE a.id=:id";
@SuppressWarnings("unchecked")
TypedQuery<Account> query = session.createQuery(hql).setParameter("id", id);
List<Account> accounts = query.getResultList();
/*
// 使用原生SQL实现
Session session = sessionFactory.openSession();
String sql = "select * from account where acct_id = ? ";
session.getTransaction().begin();
@SuppressWarnings("unchecked")
List<Account> accounts = session.createNativeQuery(sql)
.addEntity(Account.class)
.setParameter(1, id)
.list();
session.getTransaction().commit();
session.close();
*/
return accounts.isEmpty() ? null : accounts.get(0);
/*
// 使用HibernateTemplate实现
Account account = hibernateTemplate.get(Account.class, id);
return account;
*/
}
/*
多条件查询案例, 对应SQL语句:
select
this_.acct_id as acct_id1_0_0_,
this_.name as name2_0_0_,
this_.balance as balance3_0_0_
from
account this_
where
(
this_.name like ?
and this_.balance>=?
)
*/
@Override
public List<Account> findByNameAndBlance(String name, double geBalance) {
DetachedCriteria criteria = DetachedCriteria.forClass(Account.class);
criteria.add(Restrictions.and(
Restrictions.like("name", name),
Restrictions.ge("balance", geBalance)
));
// HibernateTemplate的findXXX可以通过参数控制返回数据的位置和数量(类似于MySQL的limit):
// HibernateTemplate调用的SQL是与不带参数的是一样的, 并非数据库级别的过滤。
// 从第一条记录开始, 最多返回2调记录,
int firstResult = 0, maxResults = 2;
@SuppressWarnings("unchecked")
List<Account> accounts = (List<Account>) hibernateTemplate.findByCriteria(criteria, firstResult, maxResults);
// List<Account> accounts = (List<Account>) hibernateTemplate.findByCriteria(criteria);
return accounts;
}
/*
插入Account数据, SQL:
insert
into
account
(name, balance)
values
(?, ?)
*/
@Transactional(readOnly = false)
@Override
public Account save(Account account) {
hibernateTemplate.save(account);
hibernateTemplate.flush();
return account;
}
/*
更新给定的Account, SQL:
update
account
set
name=?,
balance=?
where
acct_id=?
*/
@Transactional(readOnly = false)
@Override
public void update(Account account) {
hibernateTemplate.update(account);
hibernateTemplate.flush();
}
/*
删除给定的Account, SQL:
delete
from
account
where
acct_id=?
*/
@Transactional(readOnly = false)
@Override
public void delete(Account account) {
hibernateTemplate.delete(account);
hibernateTemplate.flush();
}
}
Junit测试代码:
package org.littlestar.learning.package8;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.littlestar.learning.package8.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations={"classpath:org/littlestar/learning/package8/package8-bean-config.xml"})
//@ContextConfiguration(classes = HibernateConfig.class)
public class SpringTestPackage8 {
@Autowired
AccountDao accountDao;
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account.toString());
}
}
@Test
public void testFindAccountById() {
Account a = accountDao.findAccountById(3);
if (Objects.isNull(a)) {
System.out.println("no found~");
} else {
System.out.println(a.toString());
}
}
@Test
public void testFindByNameAndBlance() {
List<Account> accounts = accountDao.findByNameAndBlance("Tuzki", 1000.0D);
for (Account account : accounts) {
System.out.println(account.toString());
}
}
public static Account newAccount() {
Account newAcct = new Account();
//newAcct.setId(1L);
newAcct.setName("Tuzki");
newAcct.setBalance(1000.0D);
return newAcct;
}
@Test
public void testSave() {
Account newAcct = newAccount();
Account savedAcct = accountDao.save(newAcct);
System.out.println("New account saved, id=" + savedAcct.getId());
}
@Test
public void testUpdate() {
Account acct = accountDao.findAccountById(3);
acct.setName("newName"+new Random().nextInt(100));
accountDao.update(acct);
}
@Test
public void testDelete() {
Account newAcct = newAccount();
newAcct = accountDao.save(newAcct);
System.out.println("delete account (" + newAcct.getId()+")");
accountDao.delete(newAcct);
}
}
3.2. Spring使用配置类和注解集成Hibernate
使用配置文件来进行O/R对象关系映射太麻烦,且不直观,Hibernate是支持使用注解来定义映射的,Spring的容器也是支持配置类进行配置的。下面是与配置文件等效的配置方法。
package org.littlestar.learning.package8;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.cfg.Environment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@EnableTransactionManagement
@ComponentScan("org.littlestar.learning.package8")
@PropertySource({ "classpath:org/littlestar/learning/package8/datasource.properties" })
public class HibernateConfig {
@Value("${spring.jdbc.driverClassName}")
private String driverClassName;
@Value("${spring.jdbc.url}")
private String url;
@Value("${spring.jdbc.username}")
private String username;
@Value("${spring.jdbc.password}")
private String password;
@Value("${spring.jdbc.connectionTestQuery}")
private String connectionTestQuery;
@Value("${spring.jdbc.maxPoolSize}")
private int maxPoolSize;
@Bean(name = "hikariDataSource", destroyMethod = "close")
public DataSource hikariDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setJdbcUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setConnectionTestQuery(connectionTestQuery);
dataSource.setMaximumPoolSize(maxPoolSize);
return dataSource;
}
@Autowired
@Qualifier("hikariDataSource")
DataSource hikariDataSource;
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(hikariDataSource);
//使用注解进行O/R映射
sessionFactory.setPackagesToScan("org.littlestar.learning.package8.entity"); // 扫描实体类(@Entity)的包位置
//使用传统的XML文件进行O/R映射
//sessionFactory.setMappingLocations(new ClassPathResource("org/littlestar/learning/package8/account.hbm.xml"));
Properties hibernateProperties = hibernateProperties();
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(Environment.FORMAT_SQL, "true");
hibernateProperties.setProperty(Environment.SHOW_SQL, "true");
hibernateProperties.setProperty(Environment.DIALECT, "org.hibernate.dialect.MySQL8Dialect");
hibernateProperties.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "org.springframework.orm.hibernate5.SpringSessionContext");
return hibernateProperties;
}
@Autowired
private LocalSessionFactoryBean sessionFactory;
@Bean
public HibernateTemplate hibernateTemplate() {
HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory.getObject());
return hibernateTemplate;
}
@Bean(name = "transactionManager")
public HibernateTransactionManager hibernateTransactionManager() {
return new HibernateTransactionManager(sessionFactory.getObject());
}
}
关系映射通过JPA注解,定义在实体类中。
package org.littlestar.learning.package8.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import com.google.gson.Gson;
@Entity
@Table(name = "account")
public class Account implements Serializable {
private static final long serialVersionUID = -242844598250198468L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "acct_id", nullable = false)
private long id;
@Column(name="name", length=16)
private String name;
@Column(name="balance",length=16)
private double balance;
public Account() {}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
Gson gson = new Gson();
return gson.toJson(this);
}
}
Hibernate对象之间关联
对于关系型数据库,在数据库设计上通常会遵循数据库范式的规范,这意味这我们通常需要进行多表的关联查询,参照关系型数据库,Hibernate也支持4种关联映射: 一对一(@OneToOne), 一对多(@OneToMany), 多对一(@ManyToOne)和多对多(@ManyToMany)。
简单学习部分用法, 数据库新建一张表location,并为account表添加一个location_id字段与之关联:
location_id|city |
-----------+---------+
0|Beijing |
1|Shanghai |
2|Guangzhou|
3|Shenzhen |
对于account的每条记录对应location表的一条记录,所以是一对一关系:
@Entity
@Table(name = "account")
public class Account implements Serializable {
...
@Column(name="location_id", nullable = true)
private int locationId;
@OneToOne
@JoinColumn(name = "location_id", referencedColumnName = "location_id", nullable = true, insertable = false, updatable = false)
private Location location;
//getter, setter
}
对于location的每一条记录,对应accounts有多条记录, 所以是一对多关系:
@Entity
@Table(name = "location")
public class Location {
@Id
@Column(name = "location_id", nullable = false)
private int locationId;
@Column(name="city")
private String city;
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name="location_id")
private Set<Account> accounts;
// getter, setter
}
编写测试代码使用:
...
@RunWith(SpringRunner.class)
//@ContextConfiguration(locations={"classpath:org/littlestar/learning/package8/package8-bean-config.xml"})
@ContextConfiguration(classes = HibernateConfig.class)
public class SpringTestPackage8 {
@Autowired private SessionFactory sessionFactory;
@Autowired private HibernateTemplate hibernateTemplate;
@Test
@Transactional
public void testOneToOne() {
long accountId = 3L;
/* 使用HQL的join实现
Session session = sessionFactory.getCurrentSession();
String hql = "from Account as acct inner join acct.location as location WHERE acct.id=:id";
List<?> list = session.createQuery(hql).setParameter("id", accountId).list();
for(int i=0; i<list.size(); i++) {
Object[] row = (Object[]) list.get(i);
Account a = (Account) row[0];
//Location l = (Location) row[1];
System.out.println("Account: " +a.getName()+", Location: " + a.getLocation().getCity());
}
*/
Account account = hibernateTemplate.get(Account.class, accountId);
System.out.println("Account: " +account.getName()+", Location: "+account.getLocation().getCity());
}
@Test
@Transactional
public void testOneToMany() {
int locationId = 0;
/* 使用HQL的join实现
Session session = sessionFactory.getCurrentSession();
String hql = "from Location as loc inner join loc.accounts as accts WHERE loc.locationId=:id";
List<?> list = session.createQuery(hql).setParameter("id", locationId).list();
for(int i=0; i<list.size(); i++) {
Object[] row = (Object[]) list.get(i);
Location l = (Location) row[0];
Account account = (Account) row[1];
System.out.println("Account: " +account.getName()+", Location: "+l.getCity());
}
*/
Location location = accountDao.findLocationById(locationId);
for(Account account: location.getAccounts()) {
System.out.println("Account: " +account.getName()+", Location: " + account.getLocation().getCity());
}
}
}