分類  >  資料庫 >

dbunit入門 (2)數據的備份與恢復

tags:    時間:2013-12-23 21:49:10
dbunit入門 (二)數據的備份與恢復
這裡講如何將我們將要測試的資料庫的表的數據進行備份,然後測試, 最後恢復資料庫數據。


這裡的核心過程就是
-> QueryDataSet獲取資料庫信息。
-> FlatXmlDataSet.write寫入資料庫數據到xml文件中。
-> 進行測試
-> 測試完成, 從xml文件恢複數據到資料庫中

這裡的幾個核心的Dbunit類:

1. org.dbunit.dataset.IDataSet
這個介面是關於存放所有table信息的,包括xml文件的table數據信息以及資料庫中讀取到的table數據信息。

2. org.dbunit.dataset.xml.FlatXmlDataSet
這個類用於讀寫上面的IDataSet所存儲的信息。
它可以從xml文件中讀取信息,並寫入到IDataSet中。
或者從IDataSet中讀取信息,並寫入到xml文件中。

3. org.dbunit.database.QueryDataSet
這個類保存所有根據sql語句查詢到的表的內容。
它通過如下的構造函數獲取IDatabaseConnection。
private final IDatabaseConnection _connection;
  public QueryDataSet(IDatabaseConnection connection)。

這個類的核心方法是addTable(), 源碼如下:
 
  /**      *  Adds a table and it's associated query to this dataset.      *      * @param tableName The name of the table      * @param query The query to retrieve data with for this table. Can be null which will select      * all data (see {@link #addTable(String)} for details)      * @throws AmbiguousTableNameException       */     public void addTable(String tableName, String query) throws AmbiguousTableNameException     {         logger.debug("addTable(tableName={}, query={}) - start", tableName, query);         _tables.add(tableName, new TableEntry(tableName, query));     }      /**      *  Adds a table with using 'SELECT * FROM <code>tableName</code>' as query.      *      * @param tableName The name of the table      * @throws AmbiguousTableNameException       */     public void addTable(String tableName) throws AmbiguousTableNameException     {         logger.debug("addTable(tableName={}) - start", tableName);         this.addTable(tableName, null);     } 


它會將select語句和自己的OrderedTableNameMap對象綁定在一起。在後面需要查詢資料庫數據時, 便會執行SQL語句。(跟hibernate的session比較類似吧)。

================================================

完整的AbstractDbUnitTestCase代碼:

package com.lj.basic.test.util;  import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException;  import junit.framework.Assert;  import org.dbunit.DatabaseUnitException; import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.database.QueryDataSet; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.dataset.xml.FlatXmlProducer; import org.dbunit.operation.DatabaseOperation; import org.junit.AfterClass; import org.junit.BeforeClass; import org.xml.sax.InputSource;  import com.lj.basic.test.util.AbstractDbUnitTestCase; import com.lj.basic.test.util.DbUtil; import com.lj.basic.util.MyLog4jLogger;   /**  * 此處我們使用lingling這個資料庫schema來專門進行測試  * alleni用來存儲我們的正式信息。  * lingling->在dbutil中配置的  -jdbc connection  * alleni-> 在beans.xml中配置的 -hibernate  * @author Administrator  *  */ public class AbstractDbUnitTestCase { 	public static IDatabaseConnection dbunitCon; 	 	 	/** 	 * tempFile用來存放備份的數據信息<br/> 	 * 當測試數據時,會將數據信息存放到這個文件中。<br/> 	 * 測試完成之後,再將數據導回資料庫。 	 */ 	private File tempFile; 	 	/** 	 * Beforeclass是在創建這個類之前所做的操作<br/> 	 * 這個方法必須為static<br/> 	 * 該方法在junit測試中會被最先調用,並且只會調用一次。 	 */ 	@BeforeClass 	public static void init() throws DatabaseUnitException{ 		//System.out.println("BEFOREclass init"); 		MyLog4jLogger.debug("AbstractDbUnitTestCase init() BeforeClass"); 		//在測試運行之前,便給dbunitCon初始化並賦值。 		dbunitCon=new DatabaseConnection(DbUtil.getCon()); 	} 	 	/** 	 * 測試完成之後,此方法便會被運行,關閉dbunitCon 	 */ 	@AfterClass 	public static void destory(){ 		try { 			if(dbunitCon!=null)dbunitCon.close(); 			 		} catch (Exception e) { 			 e.printStackTrace(); 		} 		MyLog4jLogger.debug("AbstractDbUnitTestCase destory() @AfterClass finished"); 	} 	 	/** 	 * 通過項目中的xml文件獲取測試數據<br/> 	 * @param table_name 數據表的名稱 	 * @return 返回FlatXmlDataSet類型對象,包含了xml文件中的所有內容 	 */ 	protected IDataSet createDataSet(String dataSource_name) throws DataSetException{ 		 		if(!dataSource_name.endsWith(".xml")){ 			dataSource_name+=".xml"; 		} 		InputStream is=AbstractDbUnitTestCase.class.getClassLoader().getResourceAsStream(dataSource_name); 		 		Assert.assertNotNull("dbunit的基本數據文件不存在", is); 		 		return new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is))); 	} 	 	/** 	 * 這個方法用於備份指定的幾個表名 	 * @param tname 要備份的資料庫中的表名稱 	 */ 	protected void backupCustomTable(String[] tname) throws DataSetException, IOException{ 		QueryDataSet backup=new QueryDataSet(dbunitCon); 		 		for(String name:tname){ 			//這裡將表名捆綁到QueryDataSet裡面,這樣在後面讀取資料庫的時候,就會產生對應的SQL語句。 			backup.addTable(name); 		} 		 		//將IDataSet的數據信息寫入tempFile中。 		//裡面調用了FlatXmlDataSet的write方法。 		writeBackupFile(backup); 	} 	 	 	/** 	 * 將IDataSet的數據信息寫入tempFile中。 	 * @param backupDataSet 	 */ 	private void writeBackupFile(IDataSet ds) throws DataSetException, IOException{ 		if(tempFile==null||!tempFile.exists()){ 		tempFile = File.createTempFile("backup", ".xml");} 		//System.out.println(tempFile.exists()); 		FlatXmlDataSet.write(ds, new FileWriter(tempFile)); 	} 	 	/** 	 * 不建議在Oracle資料庫中使用。因為dbunitCon.createDataSet()會獲取oracle裡面的各種表,其中很多jvm是沒有許可權修改的。 	 */ 	@Deprecated 	protected void backupAllTable() throws SQLException, DataSetException, IOException{ 	 	IDataSet ds=dbunitCon.createDataSet(); 	 //	System.out.println(ds.getTable("t_user").getRowCount()); 		 	//	QueryDataSet qds=new QueryDataSet(dbunitCon); 		  		writeBackupFile(ds); 		 	} 	  	 	protected void backupOneTable(String tname) throws DataSetException, IOException{ 		this.backupCustomTable(new String[]{tname}); 	} 	 	/** 	 *  讀取tempFile里的數據,從而恢復資料庫原始的信息。 	 */ 	protected void resumeTable() throws FileNotFoundException, DatabaseUnitException, SQLException{ 		IDataSet ds=new FlatXmlDataSet(new FlatXmlProducer(new InputSource(new FileInputStream(tempFile)))); 		DatabaseOperation.CLEAN_INSERT.execute(dbunitCon, ds); 	} 	 	 	 	 } 




當我們新建一個測試代碼的時候, 只要讓該代碼繼承AbstractDbUnitTestCase就行了。

簡例如下:

package com.lj.core.dao;  import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException;  import javax.inject.Inject;  import org.dbunit.DatabaseUnitException; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.operation.DatabaseOperation; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.orm.hibernate4.SessionHolder; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.support.TransactionSynchronizationManager;  import com.lj.basic.test.util.AbstractDbUnitTestCase;    @RunWith(SpringJUnit4ClassRunner.class) //這裡的test_beans.xml中的資料庫信息必須和dbutil中通過jdbc連接的資料庫信息一致! @ContextConfiguration("/test_beans.xml")//這裡運行src/test/resources裡面的test_beans.xml,該文件指定了lingling這個測試資料庫對象 public class TestDbunit extends AbstractDbUnitTestCase {	 	@Inject 	private IUserDao userDao; 	 	 	@Inject 	private SessionFactory sessionFactory; 	 	@Before 	public void setUp() throws DatabaseUnitException, SQLException, IOException{ //		Session s=sessionFactory.openSession(); //		TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s)); //		  		//這裡的t_user就是t_user.xml,包含了我們要進行測試的信息 		IDataSet ds=createDataSet("test_user"); 		 		//備份資料庫信息,Oracle下不能使用該方法,要指定table名來備份 		//this.backupAllTable(); 		this.backupOneTable("t_user"); //		  		DatabaseOperation.CLEAN_INSERT.execute(dbunitCon, ds);  		  		 		 	} 	 	@Test 	public void testUser(){ 		//test code 	} 	 	 	@After 	public void tearDown() throws FileNotFoundException, DatabaseUnitException, SQLException{ 		//teardown code 		 this.resumeTable(); 	} 	 }





============================================

最後還是要說, 這東西不熟練的情況下還是不要和運行資料庫混淆。
測試就用測試的資料庫, 網站運行就用運行的資料庫。
雖然dbunit可以備份恢複數據, 但是一旦出了差錯,還是非常麻煩的。

推薦閱讀文章

Bookmark the permalink ,來源:互聯網