Skip to content

自动数据持久化应用,与db_tools配套使用

License

Notifications You must be signed in to change notification settings

dong50252409/db

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

db

Erlang的自动数据库持久化应用,当前仅支持MySQL,与 db_tools 配套使用

本应用使用到了以下第三方Erlang开源库

  • mysql-otp MySQL驱动,默认安装
  • poolboy 进程池,默认安装
  • jsx JSON序列化反序列化,当需要将JSON转为Erlang项式时安装,默认不安装

主要模块

db_state 进程State持久化管理
db_ets ETS表持久化管理
db_ets_transform 处理注册ETS表的函数模块替换
db_mysql MySQL增删改查API封装
db_util 一些工具函数
db_model Model文件回调函数定义

配置及使用

  1. 通过 db_tools 生成数据库表model文件

  2. 添加依赖到rebar.config

 {deps, [
      {db, {git, "https://github.com/dong50252409/db", {branch, "master"}
 ]}.
  1. 配置数据库参数
{mysql_pool, [
    %% {数据库连接池名,{[poolboy的参数], [mysql-otp的参数]}}
    {db_pool, {[                  
        {size, 10},               
        {max_overflow, 20},
        {strategy, fifo}
    ], [
        {user, "root"},           
        {password, "root"},
        {host, "localhost"},
        {database, "test_db"},
        {port, 3306}
    ]}}
]}.

db_state使用方法

  1. 使用db_state:reg/3注册要管理的数据库表模块,或者通过db_state:reg_select/4注册要管理的数据库表模块,并查数据库表
  2. 使用db_state:flush/2更新同步数据到数据库表中
  3. 使用db_state:reg/3例子
State = #{},
DBPool = db_pool,
ModName = table_1,
Options = [{struct_type, map}],
ok = db_state:reg(DBPool, ModName, Options),
Table1 = #{field_1 => 1, field_2 => 100},
State = #{ModName => Table1},
_Ref = erlang:send_after(1000 * 60, self(), {db_flush, ModName}), % 一分钟后触发
receive
  {db_flush, ModName} ->
    db_state:flush(ModName, maps:get(ModName, State))
end.
  1. 使用db_state:reg_select/4例子
State = #{},
DBPool = db_pool,
ModName = table_1,
Conditions = [{field_1, '=', 100}],
Options = [{struct_type, map}],
{ok, Table1} = db_state:reg_select(DBPool, ModName, Conditions, Options),
NewTable1 = Table1#{field_2 := 100},
_Ref = erlang:send_after(1000 * 60, self(), {db_flush, ModName}), % 一分钟后触发
receive
  {db_flush, ModName} ->
    db_state:flush(ModName, maps:get(ModName, State))
end.

db_ets使用方法

  1. 使用db_ets:reg/4注册要管理的ETS表,使用db_ets:init_insert/2插入数据库中已存在的数据,或通过db_ets:reg_select/5注册要管理的ETS表,并查找数据并自动插入ETS表
  2. 使用db_ets:flush/1更新同步数据到数据库表中
  3. 使用db_ets:pull/1获取脏数据列表
  4. 实例1,通过db_ets:reg/4管理ETS表
ModName = table_1,
TableName = ModName:get_table_name(),
DBPool = db_pool,

{ok, _Columns, Rows}  = db_mysql:select(DBPool, TableName, []),
Records = [ModName:as_record(Row) || Row <- Rows],

Options = [{mode, auto}],
Tab = ets:new(ets_table_1, [named_table, set, {keypos, #table_1.field_1}]),
ok = db_ets:reg(Tab, DBPool, ModName, Options),
db_ets:init_insert(Tab, Records),

ets:update_element(Tab, 1, {#table_1.field_2, 200}),
ok.
  1. 实例2,通过db_ets:reg_select/5管理ETS表
ModName = table_1,
DBPool = db_pool,
Conditions = [],
Options = [{mode, auto}],
Tab = ets:new(ets_table_1, [named_table, set, {keypos, #table_1.field_1}]),
ok = db_ets:reg_select(Tab, DBPool, ModName, Conditions, Options),
ets:update_element(Tab, 1, {#table_1.field_2, 200}),

  1. 实例3,通过设置回调模块定时接收脏数据列表,并做更新处理
ModName = table_1,
DBPool = db_pool,
Conditions = [],
Options = [{mode, {callback, ?MODULE}}],
Tab = ets:new(ets_table_1, [named_table, set, {keypos, #table_1.field_1}]),
ok = db_agent_ets:reg_select(Tab, DBPool, ModName, Conditions, Options),

Records = [#table_1{field_1 => 1, field_2 => 100}, #table_1{field_1 => 2, field_2 => 500}],
ets:insert(Tab, Records),

receive
  {ets_dirty_list, InsertKeyList, UpdateKeyList, DeleteKeyList} ->
    ok  % 执行自己的同步逻辑
end.

进阶使用

  1. 实现db_ets_transform.erl中规定的回调函数
-module(xx_callback).

-behavior(db_ets_transform).

-export([reg_list/0]).

reg_list() ->
    ETSTab1 = ets_table_1,
    ETSTab2 = ets_table_2,
    DBPool = db_pool,
    ModeName1 = table_1,
    ModeName2 = table_2,
    Conditions = [],
    Options = [{mode, auto}, {flush_interval, 5000}],
    [
        {ETSTab1, DBPool, ModeName1, Conditions, Options},    % 注册ETS表,并根据Conditions自动从数据库中读取数据,并插入到给定ETS表中
        {ETSTab2, DBPool, ModeName2, Options}                 % 仅注册ETS表
    ].
  1. 追加以下内容到rebar.config
{erl_opts, [
    {parse_transform, db_ets_transform},                        % 增加parse_transform编译选项
    {db_ets_callback, xx_callback},                             % 配置回调模块名
    db_ets_verbose                                              % 如果需要显示parse_transform替换函数详情,则定义此值
]}.

{erl_first_files, ["src/xx_callback.erl"]}.                     % 确保回调模块在被db_ets_transform模块调用前被编译完成
  1. 使用过程中无需显式调用db_ets:xx相关函数,编译过程中由db_ets_transform.erl模块自动处理。 注意!!! 调用ets:xx相关函数的第一个参数必须显式传入atom()类型的ETS表名,否则无法识别替换(实例1为错误示范)

  2. 实例1,错误示范

ETSTab = ets_table_1,
ModName = table_1,

% ETSTab变量在parse_transform过程中无法识别,ets:new/2无法进行替换
Tab = ets:new(ETSTab, [named_table, set, {keypos, #table_1.field_1}]),

%% ETSTab变量在parse_transform过程中无法识别,ets:update_element/3无法进行替换,
本次操将无法正确同步到数据库中
ets:update_element(ETSTab, 1, {#table_1.field_2, 200}),                   
ok.
  1. 正确示范
ModName = table_1,
Tab = ets:new(ets_table_1, [named_table, set, {keypos, #table_1.field_1}]),
ets:update_element(ets_table_1, 1, {#table_1.field_2, 200}),
ok.
-define(ETS_TABLE_2, ets_table_2).

ModName = table_2,
{ok, _Columns, Rows}  = db_mysql:select(DBPool, TableName, []),
Records = [ModName:as_record(Row) || Row <- Rows],

Tab = ets:new(?ETS_TABLE_2, [named_table, set, {keypos, #table_2.field_1}]),
db_ets:init_insert(?ETS_TABLE_2, Records),

ets:update_element(?ETS_TABLE_2, 1, {#table_2.field_2, 200}),
ok.

db_mysql类型

  • -type db_pool() :: poolboy:pool().

    数据库连接池

  • -type mysql_conn() :: mysql:connection().

    MySQL连接进程

  • -type table_name() :: atom().

    数据库表名,可通过ModName:get_table_name/0获取

  • -type field() :: atom().

    数据库表字段

  • -type value() :: term().

    数据库表数据

  • -type sql() :: iodata().

    SQL语句

  • -type operator() :: '='|'!='|'>'|'<'|'>='|'<='|'LIKE'|'BETWEEN'|'AND'|'OR'|'IN'|'NOT IN'.

    可用的WHERE条件

  • -type condition() :: {field(), operator(), value()}|{field(), operator(), value(), operator(), value()}|operator().

    单个WHERE条件格式,例如

    • [{field_1, '>=', 100}, 'AND', {field_1, '<=', 500}]
    • [field_1, 'BETWEEN', 100, 'AND', 500]
    • [field_1, 'IN', [100, 200, 300, 400, 500]]
  • -type affected_rows() :: non_neg_integer().

    执行SQL后的受影响行数,仅update_all/4, update_rows/5, update_rows/6, delete_rows/3, delete_rows/4操作有返回

  • -type query_error() :: {error, mysql:server_reason()}.

    执行SQL报错后返回的信息

db_state类型

  • -type option() :: {struct_type, struct_type()}.

    指定被管理表的选项,此选项为必选项

  • -type struct_type() :: map | maps | record | record_list.

    可选的数据保存形式

    • map适用于单行数据保存,将数据库表数据以map形式保存,key为表字段,value为表字段值
    • maps适用于多行数据保存,将数据库表数据以map形式保存,key为表主键列字段的值,如果存在多个主键列,则将其组织成tuple形式,value为表一行数据所组成的map
    • record适用于单行数据保存,将数据库表数据以record形式保存
    • record_list适用于多行数据保存,将数据库表数据以列表形式保存,每个元素为record
  • -type struct() :: map() | tuple() | [tuple()] | undefined.

    指定{struct_type, struct_type()} 选项后可返回的数据保存形式,对于map、tuple两种保存形式,如果数据为空返回undefined。实际开发中,如果想要删除通过map、tuple两种保存形式的数据,仅需将数据赋值为undefined,当调用 db_state:flush/2函数时将自动删除对应数据

db_ets类型

  • -type option() :: {mode, auto|{callback, module()|pid()}}|{flush_interval, timeout()}.

    db_ets可用选项

    • mode 指定运作模式,所有受管理的ETS表都将设置heir选项,以便在ETS销毁时做善后工作
      • auto 指定每个刷新间隔自动同步数据库
      • {callback, module()|pid()} 指定回调进程注册名或进程PID,每个刷新间隔发送消息{ets_dirty_list, InsertList, UpdateList, DeleteList} 到回调进程,主要用于当ETS表的protectionprivate时的一种妥协方案
    • {flush_interval, timeout()} 指定自动同步数据库或触发回调间隔,默认:5秒

db_ets_transform类型

  • -type reg_info() :: {ets:tab(), db_mysql:db_pool(), module(), [db_mysql:condition()], [db_ets:option()]}|{ets:tab(), db_mysql:db_pool(), module(), [db_ets:option()]}.

    db_ets_transform回调返回参数

自定义Model文件

实现db_model.erl文件当中的回调函数即可

db_model类型

  • -type table_name() :: atom().

    数据库表名

  • -type table_field() :: atom().

    数据库表字段

  • -type table_value() :: term().

    数据库表值

  • -callback get_table_name() -> table_name().

    获取数据库表名,此函数必须实现

  • -callback new_map() -> map().

    初始化一个新的map类型数据库表结构对象

  • -callback as_map([table_value()]) -> map().

    将数据库表结构中的一行数据转为map结构,若仅调用db_state:reg/3db_ets:reg/4等函数,则无需实现

  • -callback new_record() -> tuple().

    初始化一个新的map类型数据库表结构对象

  • -callback as_record([table_value()]) -> tuple().

    将数据库表结构中的一行数据转为record结构,若仅调用db_state:reg/3db_ets:reg/4等函数,且不使用db_ets_transform编译期替换ets函数模块,则无需实现

  • -callback get_table_field_list() -> [table_field()].

    获取数据库表结构字段列表,此函数必须实现

  • -callback get_table_key_field_list() -> [table_field()].

    获取数据库表结构主键列表,此函数必须实现

  • -callback get_table_key_values(term()) -> [table_value(), ...].

    获取数据库表结构主键值列表,此函数必须实现

  • -callback get_table_values(term()) -> [table_value(), ...].

    获取数据库表结构一行值 ,此函数必须实现

About

自动数据持久化应用,与db_tools配套使用

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages