select必须运行在transaction等activity里面,所有我们需要构造一个方法:
Java代码
??? all_females() -> ??? F = fun() ->
??? Female = #employee{sex = female, name = '$1', _ = '_'}, ??? mnesia:select(employee, [{Female, [], ['$1']}]) ??? end,
??? mnesia:transaction(F).
all_females() -> F = fun() ->
Female = #employee{sex = female, name = '$1', _ = '_'}, mnesia:select(employee, [{Female, [], ['$1']}]) end,
mnesia:transaction(F).
select表达式匹配employee表里所有的记录中sex为female的记录 该方法可以从shell里直接调用:
Java代码
??? 1> company:all_females().
??? {atomic, [\, \]}
1> company:all_females().
{atomic, [\
使用QLC
使用QLC可能比使用Mnesia方法开销更大,但是它提供了一个很好的语法
Java代码
??? Q = qlc:q([E#employee.name || E <- mnesia:table(employee), E#employee.sex == female]), ??? qlc:e(Q).
Q = qlc:q([E#employee.name || E <- mnesia:table(employee), E#employee.sex ==
female]), qlc:e(Q).
使用QLC list comprehension来访问Mnesia表时必须运行在一个transaction里:
Java代码
??? females() -> ??? F = fun() ->
??? Q = qlc:q([E#employee.name || E <- mnesia:table(employee), E#employee.sex == female]), ??? qlc:e(Q) ??? end,
??? mnesia:transaction(F).
females() -> F = fun() ->
Q = qlc:q([E#employee.name || E <- mnesia:table(employee), E#employee.sex == female]), qlc:e(Q) end,
mnesia:transaction(F).
调用QLC写的方法:
Java代码
??? company:females().
company:females().
list comprehension表达式: 1)[括号表示“构建list”
2)||表示“例如”,<-表示“从哪里取”
上面的list comprehension表示:构建list E#employee.name,E来自employee表,并且sex属性等于female
raise female salary的方法:
Java代码
??? raise_females(Amount) -> ??? F = fun() ->
??? Q = qlc:q([E || E <- mnesia:table(employee), ??? E#employee.sex == female]), ??? Fs = qlc:e(Q),
??? over_write(Fs, Amount) ??? end,
??? mnesia:transaction(F). ???
??? over_write([E|Tail], Amount) -> ??? Salary = E#employee.salary + Amount, ??? New = E#employee{salary = Salary}, ??? mnesia:write(New),
??? 1 + over_write(Tail, Amount); ???
??? over_write([], _) -> ??? 0.
raise_females(Amount) -> F = fun() ->
Q = qlc:q([E || E <- mnesia:table(employee), E#employee.sex == female]), Fs = qlc:e(Q),
over_write(Fs, Amount) end,
mnesia:transaction(F).
over_write([E|Tail], Amount) ->
Salary = E#employee.salary + Amount, New = E#employee{salary = Salary}, mnesia:write(New),
1 + over_write(Tail, Amount);
over_write([], _) -> 0.
Mnesia用户手册:三,构建Mnesia数据库
本章详细介绍了设计Mnesia数据库和编程结构的基本步骤: 1)定义schema 2)数据模型 3)启动Mnesia
4)创建新表
1,定义schema
Mnesia系统的配置在schema里描述
schema是一个特殊的表,它包含了表名、每个表的存储类型(表应该存储为RAM、硬盘或两者)以及表的位置等信息
不像数据表,schema表里包含的信息只能通过schema相关的方法来访问和修改 Mnesia提供多种方法来定义数据库schema,可以移动表、删除表或者重新配置表布局 这些方法的一个重要特性是当表在重配置的过程中可以被访问 例如,可以在移动一个表的同时执行写操作 该特性对需要连续服务的应用非常好
下面的方法是schema管理所要用到的,它们都返回一个tuple {atomic, ok}或{aborted, Reason} 1)mnesia:create_schema(NodeList)
该方法用来初始化一个新的空schema,在Mnesia启动之前这是一个强制必要的步骤
Mnesia是一个完全分布的DBMS,而schema是一个系统表,它备份到Mnesia系统的所有节点上
如果NodeList中某一个节点已经有schema,则该方法会失败 该方法需要NodeList中所有节点上的Mnesia都停止之后才执行
应用程序只需调用该方法一次,因为通常只需要初始化数据库schema一次 2)mnesia:delete_schema(DiscNodeList)
该方法在DiscNodeList节点上擦除旧的schema,它也删除所有的旧table和数据 该方法需要所有节点上的Mnesia都停止后才执行 3)mnesia:delete_table(Tab) 该方法永久删除Tab表的备份 4)mnesia:clear_table(Tab) 该方法永久删除Tab表的记录
5)mnesia:move_table_copy(Tab, From, To) 该方法将Tab表的copy从From节点移动到To节点
表的存储类型{type}保留,这样当移动一个RAM表到另一个节点时,在新节点上也维持一个RAM表
在表移动的过程中仍然可以有事务执行读和写操作 6)mnesia:add_table_copy(Tab, Node, Type) 该方法在
Node
节点上创建
Tab
表的备份,Type
参数为
ram_copies/disc_copies/disc_only_copies 7)mnesia:del_table_copy(Tab, Node)
该方法在Node节点上删除Tab表的备份,当最后一个备份被删除后,表也被删除 8)mnesia:transform_table(Tab, Fun, NewAttributeList, NewRecordName) 该方法对Tab表的所有数据更改format,它对表里所有记录调用Fun
Fun应该是一个方法,参数为记录的旧类型,返回记录的新类型,表的key不能更改
Java代码
? ?
-record(old, {key, val}). -record(new, {key, val, extra}).
? ? ? ? ? ? ?
Transformer =
fun(X) when record(X, old) -> #new{key = X#old.key, val = X#old.val, extra = 42} end,
?? {atomic, ok} = mnesia:transform_table(foo, Transformer, ?? record_info(fields, new), ?? new),
-record(old, {key, val}). -record(new, {key, val, extra}).
Transformer =
fun(X) when record(X, old) -> #new{key = X#old.key, val = X#old.val, extra = 42} end,
{atomic, ok} = mnesia:transform_table(foo, Transformer, record_info(fields, new), new),
Fun参数也可以为ignore,它表示只更新表的meta data,不推荐使用 9)mnesia:change_table_copy_type(Tab, Node, ToType) 该方法更改表的存储类型,如从RAM改为disc_table
2,数据模型
Mnesia的数据库数据由record组成,record由tuple表示
record的第一个元素是record名,第二个元素是表的key,前两个元素组成的tuple称为oid Mnesia数据模型是对关系模型的扩展,因为该模型可以在域属性里存储任意的Erlang term 例如,可以在一个属性里存储指向其他表里的oid树,而这种类型的记录在传统的关系型DBMS里很难建模
3,启动Mnesia
在启动Mnesia之前我们必须在相应的节点上初始化一个空的schema 1)Erlang系统必须启动
2)必须使用create_schema(NodeList)来定义数据库schema
当运行一个分布式系统时,可能有多个节点参与,则mnesia:start()方法必须在相应的节点上运行 典型的,在一个嵌入式环境里mnesia:start()应该为启动脚本的一部分
在一个测试环境或解释型环境里,mnesia:start()也可以从Erlang shell或其他程序里调用
初始化schema并启动Mnesia