Product Repository Refactoring:NHibernate Helper
Pada bagian sebelumnya, kita telah belajar bagaimana melakukan operasi CRUD. Kita memanggil operasi CRUD secara eksplisit melalui session. Bagaimana kalau pemanggilan itu kita buat secara implisit. Jadi, operasinya akan terlihat seperti ini
Untuk meyimpan product:
productRepository.Save(product)
Untuk memanggil kembali product dengan id:
IProduct product=productRepository.GetById(productId)
atau dengan nama:
IProduct product =productRepository.GetByName("Apple")
Untuk menghapus product:
productRepository.Remove(product)
Untuk memanggil List Product berdasarkan kategori:
IList products=productRepository.GetByCategory("Fruits")
Jadi, kita membutuhkan object ProductRepository yang didalamnya terdapat NHibernateHelper. NHibernateHelper memiliki method OpenSession.
NHibernate Helper
Pertama sekali kita buat class test baru
[TestFixture]
public class NHibernateHelperFixture
{
[Test]
public void OpenSessionTest() {
INHibernateHelper nhHelper=new NHibernateHelper();
ISession session=nhHelper.OpenSession();
Assert.IsNotNull(session);
}
}
Buat project baru untuk repository, katakanlah bernama: fatur.sample.NHibernateHelloWorld.Repository. Jika project dibuat dengan IDE dan monodevelop , biasanya sudah ada file AssemblyInfo.cs didalamnya. Karena target build untuk app, dalam project nant, membuat semua code didalam folder app menjadi 1 assembly, maka AssemblyInfo.cs tidak boleh lebih dari satu. So, kita harus mengeluarkannya dari daftar yang akan dicompile. Gunakan element exclude untuk mengeluarkan file dari daftar.
Buat interface INHibernateHelper di project Repository.
namespace fatur.sample.NHibernateHelloWorld.Repository
{
public interface INHibernateHelper
{
ISession OpenSession();
}
}
Buat class NHibernateHelper yang mengimplementasikan interface diatas.
namespace fatur.sample.NHibernateHelloWorld.Repository
{
public class NHibernateHelper:INHibernateHelper
{
public NHibernateHelper()
{
}
public ISession OpenSession()
{
return null;
}
}
}
Untuk sementara saya biarkan me-return null.
Agar bisa dicompile kita harus menambahkan reference NHibernate kedalam reference.
Sampai disini nunit mengatakan error karena null. OK, memang itu yang kita harapkan. Kita coba implemantasi langsung saja. Saya akan copikan apa yang ada didalam SetupFixture sebelumnya,
public class NHibernateHelper:INHibernateHelper
{
private ISessionFactory _sessionFactory;
private Configuration _configuration;
public NHibernateHelper()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof (Product).Assembly);
_configuration.AddXmlFile("Product.hbm.xml");
_sessionFactory = _configuration.BuildSessionFactory();
}
public ISession OpenSession()
{
return _sessionFactory.OpenSession();
}
}
Jalankan test, dan seharusnya sukses.
Create database Menggunakan Schema
Kita juga harus membuat helper bisa membantu men-generate database. Kita buat teest untuk export schema. Disini method saya beri nama “CreateDatabase” agar lebih bermakna sesuai konteks.
[Test]
public void SchemaExport()
{
INHibernateHelper nhHelper=new NHibernateHelper();
nhHelper.CreateDatabase();
}
Untuk memenuhi test ini, kita tambahkan method CreateDatabase didalam interface
public interface INHibernateHelper
{
ISession OpenSession();
void CreateDatabase();
}
dan implementasinya di class NHibernateHelper,
public class NHibernateHelper:INHibernateHelper
{
private ISessionFactory _sessionFactory;
private Configuration _configuration;
...
public void CreateDatabase()
{
}
}
Sementara kita biarkan tanpa ada implementasi apapun. Jalankan test, pastikan tidak ada error.
Sekarang kita buat implementasinya. Saya kopikan saja schema export dari test sebelumnya.
public class NHibernateHelper:INHibernateHelper
{
private ISessionFactory _sessionFactory;
private Configuration _configuration;
...
public void CreateDatabase()
{
new SchemaExport(_configuration).Execute(false, true, false, false);
}
}
Test saya jalankan. Tidak ada masalah. Good.
Repository Test Refactoring
Kembali ke class ProductRepositoryFixture. Saya tambahkan helper ke dalam class
[TestFixture]
public class ProductRepositoryFixture
{
private ISessionFactory _sessionFactory;
private Configuration _configuration;
private INHibernateHelper _hibernateHelper;
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof (Product).Assembly);
_configuration.AddXmlFile("Product.hbm.xml");
_sessionFactory = _configuration.BuildSessionFactory();
_hibernateHelper=new NHibernateHelper();
}
}
Test saya jalankan. No problem.
Next. Kita coba yang pertama, schemaexport saya ganti menjadi
[SetUp]
public void SetupContext()
{
_hibernateHelper.CreateDatabase();
}
Test saya jalankan. Sukses.
Add product
Sekarang kita coba untuk insert product. Hah, kita belum punya class repository. Ok. kita buat dulu. Untuk membuatnya kita mulai dari test. Saya remark sebagian test sebelumnya dan saya ganti dengan definsisi baru:
[Test]
public void Can_add_new_product()
{
var product= new Product {Name = "Apple", Category = "Fruits"};
IProductRepository productRepo=new ProductRepository(_hibernateHelper);
productRepo.Add(product);
/* using (ISession session = _sessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
*/
}
Dari implementasi baru diatas, kita belum memiliki IProductRepository dan ProductRepository. Tambahkan class baru di project repository interface dan class berikut ini:
Interface
namespace fatur.sample.NHibernateHelloWorld.Repository
{
public interface IProductRepository
{
void Add(IProduct product);
}
}
Class
namespace fatur.sample.NHibernateHelloWorld.Repository
{
public class ProductRepository:IProductRepository
{
private INHibernateHelper _nhHelper;
public ProductRepository(INHibernateHelper nhHelper)
{
this._nhHelper =nhHelper;
}
public void Add(IProduct product){
}
}
}
Lagi, implementasi Add saya biarkan kosong. Semua test yang memanggil add product error. Tidak apa-apa, karena memang belum kita implementasikan.
Untuk implemantasinya, saya kopikan saja code yang saya remark ke dalam method Add.
public void Add(IProduct product)
{
using (ISession session = _nhHelper.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
}
Test saya jalankan dan sukses. Bagian test yang saya remark, saya hapus.
Langkah diatas saya ulang-ulang untuk operasi lainnya. Hasilnya,
Class Test
[TestFixture]
public class ProductRepositoryFixture
{
private INHibernateHelper _hibernateHelper;
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_hibernateHelper=new NHibernateHelper();
}
[SetUp]
public void SetupContext()
{
_hibernateHelper.CreateDatabase();
}
[Test]
public void Can_add_new_product()
{
var product= new Product {Name = "Apple", Category = "Fruits"};
IProductRepository productRepo=new ProductRepository(_hibernateHelper);
productRepo.Add(product);
}
[Test]
public void Can_get_product()
{
IProduct product;
this.Can_add_new_product();
product=getProduct();
Assert.AreEqual("Apple",product.Name);
}
private IProduct getProduct()
{
IProductRepository productRepo= new ProductRepository(_hibernateHelper);
return productRepo.GetById(1L);
}
[Test]
public void Can_update_existing_product()
{
this.Can_add_new_product();
IProduct product=this.getProduct();
product.Name="Semangka";
IProductRepository productRepo=new ProductRepository(_hibernateHelper);
productRepo.Update(product);
IProduct reloadProduct=this.getProduct();
Assert.AreEqual("Semangka",reloadProduct.Name);
}
[Test]
public void Can_remove_existing_product()
{
this.Can_add_new_product();
IProduct product=this.getProduct();
IProductRepository productRepo=new ProductRepository(_hibernateHelper);
productRepo.Remove(product);
var fromDb=this.getProduct();
Assert.IsNull(fromDb);
}
[Test]
public void Can_get_existing_product_by_name()
{
this.Can_add_new_product();
IProductRepository productRepo= new ProductRepository(_hibernateHelper);
IProduct product=productRepo.GetByName("Apple");
Assert.AreEqual("Apple",product.Name);
}
[Test]
public void Can_get_existing_products_by_category()
{
this.addProducts();
IProductRepository productRepo= new ProductRepository(_hibernateHelper);
ICollection products = productRepo.GetByCategory("Fruits");
Assert.AreEqual(2,products.Count);
}
private void addProducts()
{
var apple= new Product {Name = "Apple", Category = "Fruits"};
var durian=new Product {Name="Durian", Category ="Fruits"};
var tomato=new Product {Name= "Tomato", Category = "Vegetable"};
IProductRepository productRepo= new ProductRepository(_hibernateHelper);
productRepo.Add(apple);
productRepo.Add(durian);
productRepo.Add(tomato);
}
}
Class ProductRepository
public class ProductRepository:IProductRepository
{
private INHibernateHelper _nhHelper;
public ProductRepository(INHibernateHelper nhHelper)
{
this._nhHelper =nhHelper;
}
public void Add(IProduct product){
using (ISession session = _nhHelper.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
}
public IProduct GetById(long id){
using (ISession session = _nhHelper.OpenSession())
return session.Get(1L);
}
public void Update(IProduct product){
using (ISession session = _nhHelper.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Update(product);
transaction.Commit();
}
}
public void Remove(IProduct product){
using (ISession session = _nhHelper.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Delete(product);
transaction.Commit();
}
}
public IProduct GetByName(string name){
using (ISession session=_nhHelper.OpenSession())
{
return session.CreateQuery("from Product p where p.Name=:name").SetString("name", "Apple").UniqueResult();
}
}
public ICollection GetByCategory(string name){
using (ISession session = _nhHelper.OpenSession())
{
return session.CreateCriteria(typeof(IProduct))
.Add(Restrictions.Eq("Category", "Fruits")).List();
}
}
}
Dan interface
public interface IProductRepository
{
void Add(IProduct product);
IProduct GetById(long id);
IProduct GetByName(string name);
void Update(IProduct product);
void Remove(IProduct product);
ICollection GetByCategory(string name);
}
What Next
Cara diatas sudah saya pakai beberapa kali untuk production, tetapi masih skala kecil. Ketika software skalanya membesar, kita harus berhati-hati menghandle Session Factory. Karena itu daripada menggunakan helper seperti diatas saya lebih memilih menggunakan Spring.Net yang didalamnya terdapat HibernateTemplate support–yang pada dasarnya sama dengan helper yang kita buat, tetapi dengan fasilitas yang lebih lengkap.
Berikutnya kita akan belajar mengenai collection, proxy dan lazy loading.
thanks for sharing kaka..
sayang gak nulis lagi..
smoga bisa dilanjut lagi
regards
Mochamad Fazar
September 30, 2010 pada 11:12 am
yang kek gitu dah gak jaman lagi zar, semua dah ada di spring.net HibernateTemplate.
dan kalau fazar ngerti CQRS, kita hanya butuh session doang disisi command.
mfathur
Oktober 1, 2010 pada 7:11 am
waduh belum ngerti CQRS nih,,
,,lumayan newbie kayak saya jadi terbantu
berarti kalo jaman sekarang bisa langsung pake library nya HibernateTemplate itu y..
sekalian aja kaka fathur nulis teknologi baru lagi
Mochamad Fazar
Oktober 1, 2010 pada 10:14 am