網站後端一般從下往上有Entity層(對應DB table) —> Dao層(對應DB table增刪改查各種操作)—> Service層(業務邏輯層,調用dao層和DB打交道) —> Controller層(對應各種http api,調用service)。
前後端分離開發的話,後端必須做到充分的自測,才能保證前後端聯調時不出大問題。如果Dao層使用JPA的話,其實不需要測試,除非用錯了。那麼,針對entity層和service/controller層,後端開發時怎麼自測一直是個問題縈繞在我心頭。
目前這個問題的一半找到了答案,可以使用Hibernate Session對entity類進行測試。如果entity類的字段定義或者註解不合理的話,增刪改查操作會報錯,那麼上層的service/controller也就會出錯。所以entity層的測試其實十分必要。在測試過程中,將hibernate配置成打印sql語句,並且在實際unit test的catch子句裏打印stack trace十分實用,能快速定位問題。
步驟總結如下,供日後參考:
- 首先在src/main/resource下增加配置文件hibernate.cfg.xml,配置數據庫driver,dialect,數據庫連接url,用戶名,密碼等等,記得把需要測試的entity類配置到mapping節點裏,class=“完整包名.entity類名”
有時候爲了省事兒,也可以不配置這個文件,直接在HibernaterUtil類裏,創建SessionFactory實例時把各項配置塞進去。下一步代碼裏會有示例。
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://dburl/auto</property>
<property name="hibernate.connection.username">DB用戶名</property>
<property name="hibernate.connection.password">DB密碼</property>
<property name="hibernate.connection.pool_size">1</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping class="com.ssophie.xxxentiry"/>
<mapping class="com.ssophie.oooentity"/>
</session-factory>
</hibernate-configuration>
- HibernateUtil.class
如果走hibernate.cfg.xml的話,代碼異常簡單,一句話的事兒:sessionFactory = new Configuration().configure().buildSessionFactory();
。
不走hibernate.cfg.xml的話呢,就把每個配置項塞進去:
Configuration configuration = new Configuration().configure().buildSessionFactory();
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect")
.setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver")
.setProperty("hibernate.connection.url", "DBUrl...").setProperty("hibernate.connection.username","oooo")
.setProperty("hibernate.connection.password", "xxxx");
ServiceRegistry serviceRegistry
= new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build(); configuration.addAnnotatedClass(entity_xxx.class).addAnnotatedClass(entity_ooo.class);
sessionFactory = configuration
.buildSessionFactory(serviceRegistry);
完整的HibernateUtil類代碼如下,創建SessionFactory實例之後,使用sessionFactory.openSession()就可以拿到一個session.
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import com.ssc.ssgx.web.entities.xxx;
import com.ssc.ssgx.web.entities.ooo;
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
static {
try{
loadSessionFactory();
}catch(Exception e){
System.err.println("Exception while initializing hibernate util.. ");
e.printStackTrace();
}
}
public static void loadSessionFactory(){
sessionFactory = new Configuration().configure().buildSessionFactory();
/**
Configuration configuration = new Configuration().configure().buildSessionFactory();
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect")
.setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver")
.setProperty("hibernate.connection.url", "DBUrl...").setProperty("hibernate.connection.username","oooo")
.setProperty("hibernate.connection.password", "xxxx");
ServiceRegistry serviceRegistry
= new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build(); configuration.addAnnotatedClass(entity_xxx.class).addAnnotatedClass(entity_ooo.class);
sessionFactory = configuration
.buildSessionFactory(serviceRegistry);**/
}
public static Session getSession() throws HibernateException {
Session retSession=null;
try {
retSession = sessionFactory.openSession();
}catch(Throwable t){
System.err.println("Exception while getting session.. ");
t.printStackTrace();
}
if(retSession == null) {
System.err.println("session is discovered null");
}
return retSession;
}
}
- 測試類裏,在BeforeClass裏獲取session,然後執行各個test單元,最後在AfterClass裏關閉session即可。如果是進行刪除修改測試的話,記得tansaction要commit…
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.ssc.ssgx.web.entities.CaseInfo;
public class CaseInfoTest {
private static Session session;
@BeforeClass
public static void getSession(){
session = HibernateUtil.getSession();
}
@Test
public void TestAddCase() {
Transaction transaction = null;
try {
transaction = session.beginTransaction();
CaseInfo caseInfo = new CaseInfo();
caseInfo.setCaseDesc("xxx");
caseInfo.setCaseId("xxx");
session.save(caseInfo);
transaction.commit();
} catch (Exception exception) {
System.out.println(exception.getMessage());
transaction.rollback();
}
}
@Test
public void deleteCase(){
Transaction transaction = null;
try {
transaction = session.beginTransaction();
CaseInfo c = session.get(CaseInfo.class, Long.valueOf(6l));
session.delete(c);
transaction.commit();
} catch (Exception exception) {
System.out.println(exception.getMessage());
transaction.rollback();
}
}
@AfterClass
public static void closeSession(){
if(session != null)
session.close();
}
}