[Castle ActiveRecord ] 4. CRUD

 ActiveRecord 的 CRUD 操作基本上都是由 ActiveRecordBase 的靜態方法完成。其實例(含子類型)方法,以及 ActiveRecordMediator 只是對這些靜態方法的包裝調用而已。當然,ActiveRecordBase 方法又是對 NHibernate Session 方法的包裝。

我們用下面這個實體來簡單演示一下常用方法的使用。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
    private int id;

    [PrimaryKey(PrimaryKeyType.Identity, Access=PropertyAccess.FieldCamelcase)]
    public int Id
    {
        get { return id; }
    }

    private string name;

    [Property(Unique=true, NotNull=true)]
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

爲了觀察 NHibernate 生成的 SQL 語句,我們在配置文件中添加 "show_sql" 配置。
<?xml version="1.0" encoding="utf-8" ?>
<activerecord>
    <config>
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="server=(local);uid=sa;pwd=123456;database=test" />
        <add key="hibernate.show_sql" value="true" />
    </config>
</activerecord>

1. Create

Create 通過調用 NHibernate.ISession.Save() 方法完成實體類型的存儲操作。方法調用完成後,我們就能立即獲取 PrimaryKey ID。
User user = new User();
user.Name = "User";
user.Create();

Console.WriteLine(user.Id);


SQL

NHibernate: INSERT INTO Users (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'User'


2. Update

Update 通過調用 NHibernate.ISession.Update() 方法完成實體類型的更新操作。

User user2 = User.Find(1);
user2.Name = "User2";
user2.Update();


SQL

NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '1'
NHibernate: UPDATE Users SET Name = @p0 WHERE Id = @p1
@p0 = 'User2'
@p1 = '1'


多數時候,我們先從數據庫獲取實例,然後再更新。但也可以直接創建實體對象,進行賦值更新。

// 先將上面的實體改成 Id {get; set;}
User user2 = new User(); 
user2.Id = 1;
user2.Name = "abcd";
user2.Update();


SQL

NHibernate: UPDATE Users SET Name = @p0 WHERE Id = @p1
@p0 = 'abcd'
@p1 = '1'


對比上下兩次生成的 SQL 語句,會發現後一種方法更 "快" 一些。但更新的前提是必須知道所有實體屬性的 "值",通常這是做不到的,所以用處不大。

3. Save

Save 方法比較有趣,它調用的是 NHibernate.ISession.SaveOrUpdate()。也就是說,如果是 "新" 實體,它會 Create,否則 Update。

User user = new User();
user.Name = "User";
user.Save();

Console.WriteLine(user.Id);

User user2 = User.Find(user.Id);
user2.Name = "User2";
user2.Save();

Console.WriteLine(User.Find(user.Id).Name);


 SQL

NHibernate: INSERT INTO Users (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'User'
NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '1'
NHibernate: UPDATE Users SET Name = @p0 WHERE Id = @p1
@p0 = 'User2'
@p1 = '1'
NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '1'

4. Delete

刪除實體比較簡單。
User user = new User();
user.Name = "User";
user.Save();

user.Delete();

SQL

NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '1'

哎~~~ DeleteAll 還是老樣子,要是想刪除 1000 萬個實體數據,我強烈建議你先去泡杯茶。
for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

User.DeleteAll();

SQL

NHibernate: select user0_.Id as Id, user0_.Name as Name from Users user0_
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '1'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '2'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '3'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '4'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '5'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '6'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '7'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '8'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '9'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '10'

DeleteAll 還支持集合刪除。
List<User> users = new List<User>();

for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();

    users.Add(user);
}

User.DeleteAll(new int[] { users[1].Id, users[3].Id });

SQL

NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '2'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '2'
NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '4'
NHibernate: DELETE FROM Users WHERE Id = @p0
@p0 = '4'

5. Refresh

Refresh 的作用是從數據庫刷新實體信息。
User user = new User();
user.Name = "User";
user.Save();

User user2 = User.Find(user.Id);
user2.Name = "xxxx";
user2.Save();

Console.WriteLine(user.Name);
user.Refresh();
Console.WriteLine(user.Name);

SQL

NHibernate: SELECT user0_.Id as Id0_, user0_.Name as Name0_ FROM Users user0_ WHERE user0_.Id=@p0
@p0 = '1'

6. Find

AR 提供了多種 Find 方法用來查找實體。

(1) PrimaryKey

for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

User u = User.Find(3);
Console.WriteLine(u.Name);


我們還可以用 TryFind 代替 Find。這個方法在沒找到實體時,不會觸發異常。

User u = User.TryFind(500);
Console.WriteLine(u == null);

(2) Property

for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

User[] us = User.FindAllByProperty("Name", "User3");
foreach (User u in us) Console.WriteLine(u.Name);


(3) NHibernate.Expression.ICriterion

NHibernate Criterion 是 HQL 的 "另外一種表現方式",可用來組合多種條件。常用的有 LtExpression(<)、GeExpression(>=)、EqExpression(=)、GtExpression(>)、LeExpression(<=)、LikeExpression、AndExpression、BetweenExpression、NotExpression、NotNullExpression、NullExpression 等。還可以使用 NHibernate.Express.Order 進行排序。

for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

//User[] users = User.FindAll(new Order[]{ new Order("Id", false) }, new InExpression("Id", new object[]{ 1, 3, 5 }));
//User[] users = User.FindAll(new EqExpression("Name", "User3"));
//User[] users = User.FindAll(new OrExpression(new EqExpression("Name", "User1"), new EqExpression("Name", "User3")));

//User u = User.FindFirst(new BetweenExpression("Id", 3, 6));
User u = User.FindOne(new EqExpression("Id", 3));

注意 FindOne 和 FindFirst 的區別,如果表達式返回的記錄超過 1,FindOne 會觸發異常。

7. Exists

Exists 可用來判斷實體類型是否已經創建了對應的數據表,還可以通過主鍵判斷實體是否存在,或者通過 hql 語句做出複雜的判斷。

// 判斷數據表是否存在
Console.WriteLine(User.Exists());

// 通過主鍵判斷
Console.WriteLine(User.Exists<int>(3));

// HQL
Console.WriteLine(User.Exists("Name=?", "User3"));
Console.WriteLine(User.Exists("Name=? or Id=?", "User400", 4));

8. Query

Query 和 Find 的最大區別是它對 HQL 的支持。AR 提供了三個 Query 對象:SimpleQuery、ScalarQuery、CountQuery。

SimpleQuery: 主要用於獲取一組信息,如實體數組、單個屬性數組。
for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

//SimpleQuery query = new SimpleQuery(typeof(User), "from User user where user.Name = ?", "User2");
//User[] users = (User[])User.ExecuteQuery(query);

//SimpleQuery query = new SimpleQuery(typeof(User), typeof(String), "select user.Name from User user where user.Name = ?", "User2");
//string[] names = (string[])User.ExecuteQuery(query);

//SimpleQuery<User> query = new SimpleQuery<User>("from User user where user.Name = ?", "User2");
//User[] users = query.Execute();

SimpleQuery<string> query = new SimpleQuery<string>(typeof(User), "select user.Name from User user where user.Name = ?", "User2");
string[] names = query.Execute();
ScalarQuery: 主要用於獲取單個實體對象,或單個實體的多個屬性。當返回實體記錄數大於 1 時會觸發異常。

for (int i = 0; i < 10; i++)
{
    User user = new User();
    user.Name = "User" + i;
    user.Save();
}

//ScalarQuery query = new ScalarQuery(typeof(User), "from User user where user.Name = ?", "User2");
//User u = (User)User.ExecuteQuery(query);

//ScalarQuery query = new ScalarQuery(typeof(User), "select user.Id, user.Name from User user where user.Name = ?", "User2");
//object[] properties = (object[])User.ExecuteQuery(query);

//ScalarQuery<User> query = new ScalarQuery<User>(typeof(User), "from User user where user.Name = ?", "User2");
//User u = query.Execute();

ScalarQuery<object> query = new ScalarQuery<object>(typeof(User), "select user.Id, user.Name from User user where user.Name = ?", "User2");
object properties = query.Execute();

CountQuery: 用於統計數據庫中實體記錄數量。

//CountQuery query = new CountQuery(typeof(User));
//Console.WriteLine(User.ExecuteQuery(query));

CountQuery query = new CountQuery(typeof(User), "Name like ?", "User1%");
Console.WriteLine(User.ExecuteQuery(query));

我們還可以使用重載方法中的 QueryLanguage 參數,直接執行 SQL 語句,不過要謹慎使用,因爲這樣一來,就有可能失去自由切換數據庫的能力。

9. Paging

利用 CountQuery 和 ActiveRecordBase.SlicedFindAll() 我們可以實現分頁查詢功能。
for (int i = 1; i <= 100; i++)
{
    User user = new User();
    user.Name = "User" + i.ToString().PadLeft(3, '0');
    user.Save();
}

int pageIndex = 5;
int pageSize = 10;

CountQuery countQuery = new CountQuery(typeof(User), "Id < ?", 45);
int count = (int)User.ExecuteQuery(countQuery);

if (count > 0)
{
    int pageCount = count / pageSize + (count % pageSize > 0 ? 1 : 0);
    pageIndex = Math.Min(pageIndex, pageCount);
    int first = Math.Max((pageIndex - 1) * pageSize, 0);

    User[] users = User.SlicedFindAll(first, pageSize, new LtExpression("Id", 45));
    foreach (User u in users) Console.WriteLine(u.Name);
}

SQL

NHibernate: select COUNT(*) as x0_0_ from Users user0_ where (Id<@p0)
@p0 = '45'
NHibernate: SELECT top 50 this.Id as Id0_, this.Name as Name0_ FROM Users this WHERE this.Id<@p0
@p0 = '45'


看它生成的 SQL 語句,可見其效率不是很好。只是不知道使用 SQLite / MySQL 時,是否會使用 LIMIT 語句進行分頁處理。有空研究一下……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章