SAAS 系统的痛点
1、大客户定制化程度高
2、客户对自身数据安全的担忧,如何防止数据的泄露也滥用
DawnSql 的解决方案
众所周知,软件 = 程序 + 数据。
- 如果客户的数据需要上传到 SAAS 厂商的服务器,那么 SAAS 厂商只需要对相应的数据设置用户组,并对这个用户组设置相应的权限,就可以将该用户组的权限,发给客户,客户就可以根据自己的实际需求来修改、扩展相应的功能!
- 客户对自身数据安全的担忧。客户只需要将自己的数据根据 SAAS 厂商提供的表格式部署到 DawnSql中,同时设置好用户组权限,SAAS厂商就可以使用这个用户组来访问数据,用户直接使用 SAAS厂商提供的服务即可。
DawnSql 例子
所有的例子来自于,NorthWind 实例数据库。
1、设置配置文件
1.1、设置 consistentId,如果是集群的话,每个节点的 consistentId 应该是不一样,这个属性可以不设置。(不过我们建议设置)
<property name="consistentId" value="dawn_node"></property>
1.2、设置超级管理员的 root token
<!-- 超级管理员的 root token, 用户可以直接来设置一个 root token -->
<!-- 本例中 root token 为 dafu -->
<property name="root_token" value="dafu"/>
1.3、用户是否实现了 log 事务接口。默认是没有实现,这个需要用户根据自己的实际需求来实现。
<!-- 设置实现 log 事务接口的类 -->
<!--
<property name="myLogCls" value="org.gridgain.smart.logClient.MyLogTransactionClient"/>
-->
1.4、是否实现了初始化后就执行的方法,默认是没有实现
<!-- 设置启动的时候,初始化 rpc 服务器 -->
<!-- org.dawn.rpc.MyRpcStartImpl 是实现了 IDawnSqlStart 接口的类 -->
<!-- 不设置则不启动这个服务,具体代码可以参看开源的例子 -->
<!--
<property name="startAppCls" value="org.dawn.rpc.MyRpcStartImpl"/>
-->
1.5、创建表的模板 (这个是必须要设置的)
例子中设置了两个模板:
base 模板:复制模式,支持事务
manage 模板:分区模式,同一份数据,在集群中备份 3 次,支持事务
<!-- 创建表的模板 -->
<property name="templateConfiguration">
<map key-type="java.lang.String" value-type="org.apache.ignite.configuration.TableTemplateConfiguration">
<entry key="base">
<bean class="org.apache.ignite.configuration.TableTemplateConfiguration">
<property name="templateValue" value="template=REPLICATED,ATOMICITY=TRANSACTIONAL"></property>
<property name="description" value="复制模式,来保存数据!"></property>
</bean>
</entry>
<entry key="manage">
<bean class="org.apache.ignite.configuration.TableTemplateConfiguration">
<property name="templateValue" value="template=partitioned,backups=3,ATOMICITY=TRANSACTIONAL"></property>
<property name="description" value="分片模式,来保存数据!"></property>
</bean>
</entry>
</map>
</property>
2、创建表
-- 1、产品类型表
CREATE TABLE IF NOT EXISTS public.Categories (
-- 产品类型ID
CategoryID INTEGER NOT NULL auto,
-- 产品类型名
CategoryName VARCHAR(15) NOT NULL,
-- 类型说明
Description VARCHAR,
-- 产品样本
Picture VARCHAR,
PRIMARY KEY (CategoryID)
) WITH "template=manage";
-- 2、 顾客信息表
CREATE TABLE IF NOT EXISTS public.Customers (
-- 客户ID
CustomerID VARCHAR(5) NOT NULL,
-- 所在公司名称
CompanyName VARCHAR(40) NOT NULL,
-- 客户姓名
ContactName VARCHAR(30),
-- 客户头衔
ContactTitle VARCHAR(30),
-- 联系地址
Address VARCHAR(60),
-- 所在城市
City VARCHAR(15),
-- 所在地区
Region VARCHAR(15),
-- 邮编
PostalCode VARCHAR(10),
-- 国家
Country VARCHAR(15),
-- 电话
Phone VARCHAR(24),
-- 传真
Fax VARCHAR(24),
PRIMARY KEY (CustomerID)
) WITH "template=manage";
-- 3、 产品类别表
CREATE TABLE IF NOT EXISTS public.Products (
-- 产品ID
ProductID INTEGER NOT NULL,
-- 产品名称
ProductName VARCHAR(40) NOT NULL,
-- 供应商ID
SupplierID INTEGER,
-- 类型ID
CategoryID INTEGER,
-- 数量
QuantityPerUnit VARCHAR(20),
-- 单价
UnitPrice DECIMAL(10,4) DEFAULT 0,
-- 库存数量
UnitsInStock SMALLINT(2) DEFAULT 0,
-- 订购量
UnitsOnOrder SMALLINT(2) DEFAULT 0,
-- 再次订购量
ReorderLevel SMALLINT(2) DEFAULT 0,
-- 中止
Discontinued BIT NOT NULL DEFAULT 0,
PRIMARY KEY (ProductID)
) WITH "template=manage";
-- 4、 运货商
CREATE TABLE IF NOT EXISTS Shippers (
-- 运货商ID
ShipperID INTEGER NOT NULL auto,
-- 公司名称
CompanyName VARCHAR(40) NOT NULL,
-- 联系电话
Phone VARCHAR(24),
PRIMARY KEY (ShipperID)
) WITH "template=manage";
-- 5、 供应商表
CREATE TABLE IF NOT EXISTS Suppliers (
-- 地区描述
SupplierID INTEGER NOT NULL,
-- 供应商姓名
CompanyName VARCHAR(40) NOT NULL,
-- 联系人
ContactName VARCHAR(30),
-- 联系人职务
ContactTitle VARCHAR(30),
-- 地址
Address VARCHAR(60),
-- 城市
City VARCHAR(15),
-- 销售大区域
Region VARCHAR(15),
-- 邮政编码
PostalCode VARCHAR(10),
-- 国家
Country VARCHAR(15),
-- 联系电话
Phone VARCHAR(24),
-- 传真
Fax VARCHAR(24),
-- 首页
HomePage VARCHAR,
PRIMARY KEY (SupplierID)
) WITH "template=manage";
-- 6、 雇员表,存放员工的一些基本信息
CREATE TABLE IF NOT EXISTS Employees (
-- 员工编号
EmployeeID INTEGER NOT NULL,
-- 员工姓
LastName VARCHAR(20) NOT NULL,
-- 员工名
FirstName VARCHAR(10) NOT NULL,
-- 头衔
Title VARCHAR(30),
-- 尊称
TitleOfCourtesy VARCHAR(25),
-- 出生日期
BirthDate DATETIME,
-- 雇用日期
HireDate DATETIME,
-- 家庭地址
Address VARCHAR(60),
-- 所在城市
City VARCHAR(15),
-- 所在地区
Region VARCHAR(15),
-- 邮编
PostalCode VARCHAR(10),
-- 国家
Country VARCHAR(15),
-- 住宅电话
HomePhone VARCHAR(24),
-- 分机
Extension VARCHAR(4),
-- 照片
Photo VARCHAR,
-- 备注
Notes VARCHAR NOT NULL,
-- 上级
ReportsTo VARCHAR,
-- 照片路径
PhotoPath VARCHAR(255),
-- 工资
Salary FLOAT,
PRIMARY KEY (EmployeeID)
) WITH "template=manage";
-- 7、 雇员销售区域表
CREATE TABLE EmployeeTerritories (
-- ID
ID INTEGER NOT NULL auto,
-- 员工编号
EmployeeID INTEGER NOT NULL,
-- 区域代号
TerritoryID VARCHAR(20) NOT NULL,
PRIMARY KEY (ID)
) WITH "template=manage";
-- 8、 销售小区域表
CREATE TABLE Territories (
-- 地域编号
TerritoryID VARCHAR(20) NOT NULL,
-- 地域描述
TerritoryDescription VARCHAR(50) NOT NULL,
-- 地区编号
RegionID INTEGER NOT NULL,
PRIMARY KEY (TerritoryID)
) WITH "template=manage";
-- 9、 订单详情表
CREATE TABLE OrderDetails (
-- 订单编号
OrderID INTEGER NOT NULL,
-- 产品编号
ProductID INTEGER NOT NULL,
-- 单价
UnitPrice DECIMAL(10,4) DEFAULT 0,
-- 订购数量
Quantity SMALLINT(2) DEFAULT 1,
-- 折扣
Discount REAL(8,0) NOT NULL,
PRIMARY KEY (OrderID, ProductID)
) WITH "template=manage,affinity_key=ProductID";
-- 10、 订单表
CREATE TABLE IF NOT EXISTS Orders (
-- 订单编号
OrderID INTEGER NOT NULL,
-- 客户编号
CustomerID VARCHAR(5),
-- 员工编号
EmployeeID INTEGER,
-- 订购日期
OrderDate DATETIME,
-- 预计到达日期
RequiredDate DATETIME,
-- 发货日期
ShippedDate DATETIME,
-- 运货商
ShipVia INTEGER,
-- 运费
Freight DECIMAL(10,4) DEFAULT 0,
-- 货主姓名
ShipName VARCHAR(40),
-- 货主地址
ShipAddress VARCHAR(60),
-- 货主所在城市
ShipCity VARCHAR(15),
-- 货主所在地区
ShipRegion VARCHAR(15),
-- 货主邮编
ShipPostalCode VARCHAR(10),
-- 货主所在国家
ShipCountry VARCHAR(15),
PRIMARY KEY (OrderID)
) WITH "template=manage";
3、导入数据
在 DawnSql 中导入数据有两种方式。
- 通过 JDBC 来 insert 来导入数据。
例如:
由于要插入的数据比较多,所以这里只做实例,具体的数据,我们放在文档后面的下载链接中。
INSERT INTO Categories (CategoryName, Description, Picture) VALUES('Beverages','Soft drinks, coffees, teas, beers, and ales', '');
INSERT INTO Categories (CategoryName, Description, Picture) VALUES('Condiments','Sweet and savory sauces, relishes, spreads, and seasonings', '');
用 JDBC 导入
Class.forName("org.apache.ignite.IgniteJdbcDriver");
String url = "jdbc:ignite:thin://127.0.0.1:10800/public?lazy=true&distributedJoins=true&userToken=dafu";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
stmt.executeUpdate("SET STREAMING ON; INSERT INTO public.Categories (CategoryName, Description, Picture) VALUES('Seafood','草鱼', ''), ('"+ CategoryName +"', '"+ Description +"', '"+ Picture +"')");
stmt.close();
conn.close();
- 通过 JDBC 批量导入数据。
-- 批量导入
SET STREAMING ON;
INSERT INTO public.Categories (CategoryName, Description, Picture) VALUES('Seafood','草鱼', ''), ('吴大富','金毛', '');
-- 或者
SET STREAMING ON;
INSERT INTO public.Categories (CategoryName, Description, Picture) VALUES ('Seafood','草鱼', '');
INSERT INTO public.Categories (CategoryName, Description, Picture) VALUES ('吴大富','金毛', '');
- 快速批量导入。这种方法可以绕开 SQL 解析,直接将数据存入集群。但是这种方法需要对 Dawn Sql 比较了解且具备一定的编程能力。如果想了解这种方法请联系作者。
4、添加 schema
-- 添加 schema 名字为 myy
create schema myy;
-- 或者
create SCHEMA IF NOT EXISTS myy;
5、添加用户组
-- 添加用户组
add_user_group('myy_group', 'myy_token', 'all', 'myy');
-- 通过 user_token 获取用户组
get_user_group('myy_token');
6、为用户组设置权限
为 myy_group 设置用户权限。
1、
-- 对用户组 myy_group 设置访问 public.Employees 表的权限,让它只能查询
-- EMPLOYEEID < 6 的数据
my_view('myy_group', "SELECT * from public.Employees where EMPLOYEEID < 6");
7、添加方法
-- 统计只出过一单的员工数量
function get_emp_count()
{
let rs = query_sql("SELECT COUNT(DISTINCT o.EmployeeID)
FROM public.Orders o
LEFT JOIN public.Employees e ON o.EmployeeID=e.EmployeeID");
rs.next().FIRST();
}
-- 查询只卖过一件商品
SELECT p.ProductName,COUNT(DISTINCT wo.EmployeeID)
FROM public.Orders wo
JOIN public.Employees e ON wo.EmployeeID=e.EmployeeID
JOIN public.OrderDetails od ON wo.OrderID=od.OrderID
JOIN public.Products p ON od.ProductID=p.ProductID
GROUP BY p.ProductName
HAVING COUNT(DISTINCT wo.EmployeeID) = get_emp_count();
8、用 NoSql 来描述业务流程或提升程序性能
8.1、在 to B 的系统中业务的流程和规则往往特别的复杂,业务变动大,时效性要求高
例如:在保险中,代理人的组织结构是金字塔型的,佣金项几十个,不同的产品有不同的佣金比例,代理人级别不同也有所不同。可以将代理人的em_no 作为主键,将代理的上下级、保单、佣金项作为数据,形成一个树形结构。当保单改变,或者上下级变化时候,动态修改该树。当需要计算佣金的时候,只需要扫描这棵树的佣金项即可。
8.2、在需要对海量数据高并发读写的场景下
具体用法:NoSql 的支持
9、DawnSql的 root 用户可以自己扩展方法
具体方法:自定义扩展方法