基于 HTML5 的工业互联网云平台监控机房 U 位
前言
机柜 U 位管理是一项突破性创新技术--继承了 RFID 标签(电子标签)的优点的同时,完全解决了 RFID 技术(非接触式的自动识别技术)在机房 U 位资产监控场应用景中的四大缺陷,采用工业互联网云平台监控机房 U 位的方法,具有高可靠性、高准确性、精准定位、免维护的特点,满足了 U 位级实时监控、智能运维闭环管理的需求。设备上架、下架与迁移,自动变更和实时记录,(用户评价):部署工业互联网云平台监控机房 U 位后节省了 99% 的登记变更记录的时间,而且实现了变更后数据 100% 的准确,在这之前是难以想象的,真正实现运维管理最后的工作。
代码生成
场景搭建
整个 Demo 由最左侧的树,中间部分的列表以及右边的拓扑图整体构成,为了让整个布局干净一点,这里结合 splitView 和 borderPane 两种布局方式来进行。首先将场景分为左右两个部分,左边为树,右边是列表和拓扑图的组合:
treeView = this.treeView = new ht.widget.TreeView(),// 树组件
splitView = this.splitView = new ht.widget.SplitView(treeView, null, 'h', 280);// 分割组件,将场景分为左右两个部分,左边为树组件,右边为空,左边的宽度为280,右边的组件先设置为空到时候根据具体情况分配
this.splitView.addToDOM();
布局结束记得将最外层组件的最底层 div 添加到 body 中,HT 的组件一般都会嵌入 BorderPane、SplitView 和 TabView 等容器中使用,而最外层的HT组件则需要用户手工将 getView() 返回的底层 div 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些HT预定义的容器组件,则HT的容器会自动递归调用孩子组件 invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window 的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。
为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:
addToDOM = function(){
var self = this,
view = self.getView(),//获取组件的底层 div
style = view.style;
document.body.appendChild(view);//将组件底层div添加进body中
style.left = '0';//ht 默认将所有的组件的position都设置为absolute绝对定位
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize', function () { self.iv(); }, false);//窗口大小改变事件,调用刷新函数
}
右边的拓扑图部分是在监听选中变化事件的时候更新的,当然,初始化设置的选中树上的第一个节点就触发了选中变化事件:
cms.treeView.sm().ss(cms.treeView.dm().getDatas().get(0));// 设置选中树上的第一个节点
treeView.sm().ms(function(){// 监听选中变化事件
var ld = treeView.sm().ld();// 获取最后选中的节点
if (ld) self.updateForm(ld.a('type'));
});
CMS.prototype.updateForm = function(type){
var self = this,
ld = this.treeView.sm().ld();// 获取树上选中的最后一个节点
if (type === self.TYPE_RACK_SPACE) {// 如果是在树上选中了节点,那么点击“添加机柜”就直接在树上选中的节点下生成
if (!this.rackBuild) {
this.rackBuild = new RackBuild(this);// 此类中定义了场景的中间列表部分,右边拓扑图部分以及对应的逻辑
}
this.rackBuild.setData(ld);// 在树上添加一个新的节点
this.splitView.setRightView(this.rackBuild.getHTView());// 设置分割组件右边的内容为整个场景的中间“列表”内容+右边的拓扑内容
}
}
上面代码中 splitView.setRightView 函数意为设置右侧组件,有了这个函数,我就可以动态地改变 spliteView 组件中的右侧组件了。
初始化树
既然布局布好了,就该向具体的位置添加内容了。先来看看如何向树上添加节点。首先我定义了一个初始化的树上的值 treeData,通过遍历这个数组创建树上的节点以及节点上的父子关系:
var treeData = [{
name: 'Racks',
type: 8,
children: [
{
name: 'rack1',
type: 18,
usize: 32
}, {
name: 'rack2',
type: 18
}
]
}];
CMS.prototype.loadTreeData = function(){// 加载树上的节点
var self = this;
setTimeout(function(){
var data = treeData; data.forEach(function(d) {// 遍历 treeData 数组的值
self.createData(d, null);// 第一个节点父亲为空
});
self.treeView.expandAll();// 展开树
}, 10);
}
通过 createData 函数创建节点,并给节点设置父子关系:
CMS.prototype.createData = function(data, parent){// 在树上创建一个节点
var self = this,
htData = new ht.Data(),// 新建 Data 类型节点
dm = this.treeView.dm();// 获取树的数据容器
htData.a(data);// 设置节点业务属性 data
htData.setName(data.name)// 设置节点的 name 属性
if (parent) {
htData.setParent(parent);// 设置父亲节点
}
dm.add(htData);// 将节点添加到数据容器中
if (data.children) {// 如果节点中有 children 对象
data.children.forEach(function(d){// 遍历 children 对象
self.createData(d, htData);// 再创建 children 对象中的节点作为孩子节点
});
}
return htData;
}
创建场景右边部分
眼尖的同学在前面的代码中可能注意到了一个未声明的 RackBuild 类,在此类的声明中我们将场景的右半部分主要分为左右两个部分,左边又分为上下两个部分,右边也分为上下两个部分。
这里先将整个右边的部分进行布局,下面代码中的变量 listBorder 为上图的左半部分,变量 borderPane 为上图的右半部分,至于鹰眼组件部分,是添加到在 borderPane 的上层:
listView = this.listView = new ht.widget.ListView(),// 列表组件
listForm = this.listForm = new ht.widget.FormPane(),// 表单组件
listBorder = this.listBorder = new ht.widget.BorderPane(),// 场景中间边框面板组件
gv = this.gv = new ht.graph.GraphView(),// 拓扑组件
borderPane = this.borderPane = new ht.widget.BorderPane(),
toolbar = this.toolbar = new ht.widget.Toolbar(),// 工具条组件
splitView = this.splitView = new ht.widget.SplitView(listBorder, borderPane, 'h', 220),// 分割组件
overview = this.overview = new ht.graph.Overview(gv),// 鹰眼组件
overviewDiv = overview.getView();// 获取鹰眼组件底层 div overviewDiv.style.height = '120px';// HT 的组件默认都是绝对定位的
overviewDiv.style.width = '120px';
overviewDiv.style.left = '0';
overviewDiv.style.bottom = '0';
overviewDiv.style.zIndex = 10;
borderPane.getView().appendChild(overview.getView());// 将鹰眼组件底层 div 添加到面板组件的底层 div 中 listBorder.setTopView(listForm);// 设置顶部组件
listBorder.setCenterView(listView);// 设置中间组件
listBorder.setTopHeight(32);// 设置顶部组件高度
listForm.setVPadding(2);// 设置表单顶部和顶部与组件内容的间距
listForm.setHPadding(4);// 设置表单左边和右边与组件内容的间距
listForm.addRow([// 添加一行组件
{
comboBox: {// 组合框类
labels: ['All', 'Pathch Panel', 'Switch', 'Server', 'Backbone Switch/Router'],// 设置下拉可选值对应文本
values: [-1, 5, 9, 10, 11],// 设置下拉可选值
value: -1,// 设置当前值,可为任意类型
onValueChanged: function(e) {// 值变化触发函数
var val = this.getValue();// 获取当前的值
self.listTypeFilter = val;
self.listView.ivm();// 最彻底的刷新方式
}
}
}
], [0.1], 28);// 参数二为行内元素的宽度,参数三为该行高度 borderPane.setCenterView(gv);// 设置中间组件
borderPane.setTopView(toolbar);// 设置顶部组件
borderPane.setTopHeight(32);// 设置中间组件高度
从上面的代码可以看出,splitView 为最外层组件,通过 getHTView 函数返回这个组件,在前面动态设置整个场景的右半部分的组件的时候我们就是通过设置 this.splitView.setRightView(this.rackBuild.getHTView()) 设置场景的右半部分为 rackBuild 的底层 div:
getHTView: function(){// 获取最外层组件
return this.splitView;
}
添加工具条内容
toolbar 工具条中总共的元素就三个:添加机柜,编辑机柜和删除机柜。这三个元素只需要通过 setItems 的方式添加到 toolbar 工具条组件上即可,元素的具体定义如下:
var toolbarItems = [// 工具条上三个的元素
{
icon: self.getToolbarIcon('toolbar.add.rack'),// 用的是我们前面声明过的图片
toolTip: 'Add a rack',// 文字提示显示内容
action: function(){// 点击按钮后触发的函数
self._editingRack = null;
self.addRackForm.reset();
self.addRackDialog.show();// 弹出对话框,添加一个新的机架,并填写该机架的信息
}
},{
icon: self.getToolbarIcon('toolbar.edit.rack', function(){// 判断右侧拓扑图上最后选中的节点 来决定这个图标的显示颜色(如果没有选中机柜,那么此图标显示颜色为灰色)
return self.gv.sm().ld() instanceof Rack;
}),
toolTip: 'Edit rack info',
action: function(){
var ld = self.gv.sm().ld();// 获取 gv 中最后选中的节点
if (!ld) return;
self._editingRack = ld;
self.addRackForm.v('name', ld.a('name'));// 弹出框中的 name 赋值为 ld 的业务属性 name 的值
self.addRackForm.v('usize', ld.a('usize'));// 弹出框中的 usize 赋值为 ld 的业务属性 usize 的值
self.addRackDialog.show();// 点击此按钮会出现弹出框
}
},{
icon: self.getToolbarIcon('toolbar.delete', function(){
return self.gv.sm().ld() instanceof Rack;// 判断右侧拓扑图上最后选中的节点的类型
}),
toolTip: 'Delete a rack',
action: function(){
self.handleRemoveRack();// 在拓扑图上删除机柜,并删除树上此机柜对应的节点
}
},
]
接下来只要把这个 item 添加到 toolbar 中并设置一下排布的方式即可:
toolbar.setItems(toolbarItems);// 设置工具条元素数组
toolbar.setStickToRight(true);// 设置工具条是否向右对齐排布
toolbar.enableToolTip(true);// 工具条允许文字提示
上面出现的点击 toolbar 工具条按钮触发的事件中有一个“弹出对话框”的操作,通过 this.addRackDialog.show() 来实现,addRackDialog 对象定义在 initDialog 函数中,作用为创建一个 dialog 对话框,我们设置此对话框中的内容为一个 form 表单进行显示,同时还设计了两个按钮,“OK”按钮作为执行创建/更改机柜的属性,“Cancel”按钮不执行其他操作,只是将对话框隐藏:
initDialog: function(){// 初始化点击“增改”出现的对话框
var self = this,
addRackDialog = this.addRackDialog = new ht.widget.Dialog(),
addRackForm = this.addRackForm = new FormPane(),// 此类继承于 ht.widget.FormPane
labelWidth = 72; addRackForm.addRow([// 添加行
'Name',{
id: 'name',
textField: {}
}
], [labelWidth, 0.1]); addRackForm.addRow([
'Height(U)',{
id: 'usize',
textField: {
type: 'number'
}
}
], [labelWidth, 0.1]); addRackDialog.setConfig({// 配置对话框的标题,尺寸,内容等
title: "New Rack",// 对话框的标题
content: addRackForm,// 指定对话框的内容
width: 320,// 指定对话框的宽度
height: 220,// 指定对话框的高度
draggable: true,// 指定对话框是否可拖拽调整位置
closable: true,// 可选值为true/false,表示是否显示关闭按钮
resizeMode: "none",// 鼠标移动到对话框右下角可改变对话框的大小 none 表示不可调整宽高
buttons: [// 指定对话框按钮组内容
{
label: "Ok",// 按钮显示文本
action: function(button, e) {// action为回调函数,当此按钮被当点击时,回调函数会执行
var formData = addRackForm.getValueObject(), rack;
if (!formData.usize) {// 如果没有填写 Height 的值,则默认高度为18
formData.usize = 18;
}
if (self._editingRack) {// 如果是“编辑rack信息”的弹框
rack = self._editingRack;
rack.a(formData);
rack.a('treeNode').a(rack.getAttrObject());//
}
else {// “增加”新的机柜
rack = self.createRack(formData);// 创建一个新的 rack 模型
self.gv.dm().add(rack);// 在拓扑图上添加这个rack
// update tree
formData.type = self.cms.TYPE_RACK;
var treeNode = self.cms.createData(formData, cms.treeView.sm().ld());
rack.a('treeNode', treeNode);
}
self.gv.fitContent(1);// 添加元素之后,让所有的图元显示在界面上
addRackDialog.hide();// 隐藏对话框
}
}, {
label: 'Cancel',
action: function(){
addRackDialog.hide();// 隐藏对话框
}
}
],
buttonsAlign: "right"
});
}
上面代码出现的 FormPane 类,继承于 ht.widget.FormPane 类,在 htwidget.FormPane 的基础上修改也增加了一些函数,主要的内容还是 ht.widget.FormPane 的实现,文章篇幅有限,这里就不贴代码了,有兴趣的可以参考 FormPane.js 文件。
实现了添加和编辑机柜的两个功能,删除机柜的功能实现上非常容易,只要将节点从拓扑图和树上移除即可:
handleRemoveRack: function(){// 在拓扑图上删除机柜,并删除树上此机柜对应的节点
var ld = this.gv.sm().ld();// 获取 gv 上选中的最后一个节点
if (ld && ld instanceof Rack) {// 机柜是 Rack 类型
this.cms.treeView.dm().remove(ld.a('treeNode'));// 移出树上的有 treeNode 属性的节点
this.gv.dm().remove(ld);// 删除 gv 中的节点
}
}
列表中元素拖拽
所有的内容都创建完毕,接下来要考虑的就是交互的内容了。列表组件中有 handleDragAndDrop 函数实现拖拽的功能:
listView.handleDragAndDrop = this.handleListDND.bind(this);// 列表上拖拽事件监听
handleListDND: function(e, state){// 拖拽listView列表组件中的事件监听
var self = this,
listView = self.listView,
gv = self.gv,
dm = gv.dm(),
dnd = self.dnd; // handleDragAndDrop 函数有 prepare-begin-between-end 四种状态
if (state ==='prepare') {
var data = listView.getDataAt(e);// 传入逻辑坐标点或者交互event事件参数,返回当前点下的数据元素
listView.sm().ss(data);// 在拖拽的过程中设置列表组件中的被拖拽的元素被选中
if (dnd && dnd.parentNode) {
document.body.removeChild(dnd);
}
dnd = self.dnd = ht.Default.createDiv();// 创建一个 div
dnd.style.zIndex = 10;
dnd.innerText = data.getName();
}
else if (state === 'begin') {
if (dnd) {
var pagePoint = ht.Default.getPagePoint(e);// 返回页面坐标
dnd.style.left = pagePoint.x - dnd.offsetWidth * 0.5 + 'px';
dnd.style.top = pagePoint.y - dnd.offsetHeight * 0.5 + 'px';
document.body.appendChild(dnd)
}
}
else if (state === 'between') {
if (dnd) {
var pagePoint = ht.Default.getPagePoint(e);
dnd.style.left = pagePoint.x - dnd.offsetWidth * 0.5 + 'px';
dnd.style.top = pagePoint.y - dnd.offsetHeight * 0.5 + 'px';
self.showDragHelper(e);
}
}
else {// 拖拽“放开”鼠标后的操作
if (ht.Default.containedInView(e, self.gv)) {// 判断交互事件所处位置是否在View组件之上
if (dm.contains(self.dragHelper)) {// 判断容器是否包含该data对象
var rect = self.dragHelper.getRect(),// 获取图元的矩形区域(包括旋转)
target = self.showDragHelper(e),//
node,
ld = self.listView.sm().ld(),
uindex = target.getCellIndex(rect.y);
node = self.createPane(rect, ld.getAttrObject(), target, uindex);// 创建设备
dm.add(node);
// update tree data
var treeNode = self.cms.createData(ld.getAttrObject(), target.a('treeNode'));// 在树上创建节点,并设置父亲节点
treeNode.a('uindex', uindex);
node.a('treeNode', treeNode); dm.remove(self.dragHelper);
}
}
document.body.removeChild(dnd);
self.dnd = null;
}
}
设备拖动
既然有了从列表组件上拖拽下来的交互动作,接下来应该是做设备在机柜上的拖拽改变位置的功能了,我们通过监听拓扑组件 gv 的交互事件来对节点移动进行事件处理:
gv.mi(this.handleInteractor.bind(this));// 监听交互
handleInteractor: function(e){// 移动机柜中的设备 的事件监听
if (e.kind.indexOf('Move') < 0) return;// 如果非move事件则直接返回不做处理 var self = this,
listView = self.listView,
gv = self.gv,
dm = gv.dm(),// 获取数据容器
target = gv.sm().ld(),// 获取最后选中的节点
uHeight = target.a('uHeight') || 1;// target.a('uHeight')获取最后选中的节点的高度 if (e.kind === 'prepareMove') {// 准备移动
self._oldPosition = target.p();// 获取节点当前的位置
}
else if (e.kind === 'betweenMove') {// 正在移动
self.showDragHelper(e.event, uHeight);
dm.sendToTop(target);// 将data在拓扑上置顶,显示在最顶层 不会被别的节点遮盖
}
else if (e.kind === 'endMove') {// 结束移动
var rack = self.showDragHelper(e.event, uHeight);
if (dm.contains(self.dragHelper)) {// 判断容器是否包含该data对象
target.p(self.dragHelper.p());// 设置节点的坐标
target.a('uindex', rack.getCellIndex(target.p().y));// 设置节点的业务属性 uindex
dm.remove(self.dragHelper);// 移除
self._savable = true;
self.toolbar.iv();
target.setHost(rack);// 设置宿主节点
target.setParent(rack);// 设置父亲节点
// update tree
var treeNode = target.a('treeNode');// 获取拓扑图上对应的树上的节点
treeNode.setParent(rack.a('treeNode'));
}
else {
target.p(self._oldPosition);
}
}
}
代码中的 showDragHelper 就是在设备拖动的过程中,显示在机柜上,设备下的作为占位的绿色的矩形,为了方面看到当前移动的位置在机柜上显示的位置。有兴趣的可以自己了解一下,篇幅有限,这里就不提了。
列表组件过滤
会不会有同学对列表栏顶部的 form 表单做过滤有些好奇?这块代码非常简单,只需要对选中的类型进行过滤即可:
listView.setVisibleFunc(function(data){// 设置可见过滤器
if (!self.listTypeFilter || self.listTypeFilter === -1)
return true;
return data.a('type') === self.listTypeFilter;// 根据节点的自定义属性 type 来判断节点属于哪个类型 返回与当前 form 表单中选中的名称相同的所有节点进行显示
});
主要的代码就解释到这里,其他部分的内容有兴趣的同学可以自己去抠代码了解。
工业互联网可视化监控除了 2D 机房的监控,当然还有 3D 的,涉及到工业生产以及建设的方方面面,目前 3D 的占比比较重,比较直观,视觉冲击也相对来说比较强,比如动车的整体构造体现:
利用工业互联网监控的 3D 工业园:
工业互联网云平台可涉及到工业生产的方方面面,应用在工厂以及内部设备工作运行以及所回传数据的可视化,可以将任一时间段的数据以轨迹图、仪表盘等各种方式加以展现,工业互联网云平台可以广泛应用于智慧城市的各个领域,除了本例中展示的机房 U 位监控,拓扑图展示,数据的传输意外,其他的工业领域,三维建模的展示以及能源和通讯领域中,甚至轨道交通,医疗领域此工业互联网云平台都可应用。
基于 HTML5 的工业互联网云平台监控机房 U 位的更多相关文章
- 基于 HTML5 的工业互联网 3D 可视化应用
工业企业中生产线处于高速运转,由工业设备所产生.采集和处理的数据量远大于企业中计算机和人工产生的数据,生产线的高速运转则对数据的实时性要求也更高.破解这些大数据就是企业在新一轮制造革命中赢得竞争力的钥 ...
- 基于 HTML5 结合工业互联网的智能飞机控制
前言 从互联网+的概念一出来,就瞬间吸引了各行各业的能人志士,想要在这个领域分上一杯羹.现在传统工业生产行业运用互联网+的概念偏多,但是在大众创业万众创新的背景下,“互联网+”涌出了层出不穷的“玩法” ...
- 基于 HTML5 的 WebGL 自定义 3D 摄像头监控模型
前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...
- 【原创】基于Docker的CaaS容器云平台架构设计及市场分析
基于Docker的CaaS容器云平台架构设计及市场分析 ---转载请注明出处,多谢!--- 1 项目背景---概述: “在移动互联网时代,企业需要寻找新的软件交付流程和IT架构,从而实现架构平台化,交 ...
- 基于 HTML5 WebGL 的加油站 3D 可视化监控
前言 随着数字化,工业互联网,物联网的发展,我国加油站正向有人值守,无人操作,远程控制的方向发展,传统的人工巡查方式逐渐转变为以自动化控制为主的在线监控方式,即采用数据采集与监控系统 SCADA.SC ...
- 视频私有云实战:基于Docker构建点播私有云平台
私有云是为一个客户单独使用而构建的,因而提供对数据.安全性和服务质量的最有效控制.前置条件是客户拥有基础设施,并可以使用基础设施在其上部署应用程序.其核心属性是专有的资源.本篇文章将会结合网易云信的实 ...
- 基于zookeeper集群的云平台-配置中心的功能设计
最近准备找工作面试,就研究了下基于zookeeper集群的配置中心. 下面是自己设想的关于开源的基于zookeeper集群的云平台-配置中心的功能设计.大家觉得哪里有问题,请提出宝贵的意见和建议,谢谢 ...
- 基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
前言 在 3D 机房数据中心可视化应用中,随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的 ...
- 腾讯基于Kubernetes的企业级容器云平台GaiaStack (转)
GaiaStack介绍 GaiaStack是腾讯基于Kubernetes打造的容器私有云平台.这里有几个关键词: 腾讯:GaiaStack可服务腾讯内部所有BG的业务: Kubernetes:Gaia ...
随机推荐
- HDFS架构及原理
原文链接:HDFS架构及原理 引言 进入大数据时代,数据集的大小已经超过一台独立物理计算机的存储能力,我们需要对数据进行分区(partition)并存储到若干台单独的计算机上,也就出现了管理网络中跨多 ...
- gulp基本设置
var gulp = require('gulp'); var clean = require('gulp-clean'); var concat = require('gulp-concat'); ...
- 第24章 退出 - Identity Server 4 中文文档(v1.0.0)
注销IdentityServer就像删除身份验证cookie一样简单,但是为了完成联合注销,我们必须考虑将用户从客户端应用程序(甚至可能是上游身份提供者)中签名. 24.1 删除认证 要删除身份验证c ...
- [日常] Go-逐行读取文本信息
go逐行读取文本信息:1.os包提供了操作系统函数的不依赖平台的接口,Open方法打开一个文件用于读取,func Open(name string) (file *File, err error)2. ...
- C#提取PPT文本——提取SmartArt中的文本、批注中的文本
提取文本的情况在工作和学习中常会遇到,在前面的文章中,已经讲述了如何提取PPT中文本框里的文本,在本篇文章中,将介绍如何使用C#代码语言提取PPT文档中SmartArt和批注中的文本.同样的,程序里面 ...
- java实现字符串数字部分自增
实现添加员工时对工号进行自增长 思路:后台获取数据库中最后一条员工数据的工号,对其进行自增再传入前端 mybatis映射文件:获取最后一条数据 <select id="getLastN ...
- Selenium自动化 Xpath-元素定位
最近在教妹子做自动化测试,妹子基础差,于是想到很多初学自动化的朋友们学习的知识没有规范化,信息太过杂乱.所以,本文整理了一些自动化元素定位方式: 这次将讲Xpath定位! 什么是Xpath: Path ...
- 关于Windows系统不会变慢的设想
记录软件安装的过程,比如创建了哪些服务,哪些计划任务以及启动项等等. 然后软件安装完成后把关于软件的进程,服务,计划任务等都删掉. 然后手动创建一个脚本,用脚本代替软件的启动.比如,如果要启动sqls ...
- 如何程序化的构造Hibernate配置 // How to initialize Hibernate programmably
Java为什么被人诟病,因为一切都是过度设计.Hibernate其实就是实现了一套JPA的ORM,不过用极度冗赘的配置方式,nodejs Sequelize.js,甚至Python SQLAlchem ...
- net view 提示6118错误 解决方法。
1.win+R ,输入services.msc 开启服务:Server ,WorkStation,computer Browser 2.如果你的电脑没有computer Browser服务,win+R ...