以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
在本系列关于使用以太坊构建DApps教程的第6部分中,我们通过添加投票,黑名单,股息分配和撤销来完成DAO合约,同时投入一些额外的辅助函数以实现良好的标准。在本教程中,我们将构建一个用于与我们的故事Story交互的Web界面,否则我们无法统计用户如何参与。所以这是我们故事Story发布之前的最后一部分。
由于这不是一个Web应用程序教程,我们将保持非常简单。下面的代码不是生产就绪的,只是作为如何将JavaScript连接到区块链的概念证明。但首先,让我们添加一个新的迁移。
自动转移
现在,当我们部署代币和DAO时,它们位于区块链上但不进行交互。为了测试我们构建的内容,我们需要手动将代币所有权和余额转移到DAO,这在测试期间可能很乏味。
让我们写一个新的迁移,为我们做这件事。创建文件4_configure_relationship.js
并将以下内容放在其中:
var Migrations = artifacts.require("./Migrations.sol" );
var StoryDao = artifacts.require("./StoryDao.sol" );
var TNSToken = artifacts.require("./TNSToken.sol" );
var storyInstance, tokenInstance;
module.exports = function (deployer, network, accounts) {
deployer.then(function () {
return TNSToken.deployed();
}).then(function (tIns) {
tokenInstance = tIns;
return StoryDao.deployed();
}).then(function (sIns) {
storyInstance = sIns;
return balance = tokenInstance.totalSupply();
}).then(function (bal) {
return tokenInstance.transfer(storyInstance.address, bal);
})
.then(function (something) {
return tokenInstance.transferOwnership(storyInstance.address);
});
}
复制代码
这是这段代码的作用。首先,你会注意到它是基于promise的。它充满了各种调用。这是因为我们在调用下一个数据之前依赖于返回一些数据的函数。所有合约调用都是基于promise的,这意味着它们不会立即返回数据,因为Truffle需要向节点请求信息,因此promise在将来返回数据。我们强制代码等待这些数据,使用then
关键词并提供所有then
调用函数,这些函数在最终给出时将使用此结果调用。
所以,按顺序:
首先,向节点询问已部署代币的地址并将其返回。
然后,接受此数据,将其保存到全局变量中,并询问已部署的DAO的地址并将其返回。
然后,接受这些数据,将其保存到全局变量中,并询问代币合约的所有者将在其帐户中具有的余额,这在技术上是总供应量,并返回此数据。
然后,一旦你得到这个余额,用它来调用这个代币的transfer函数,并将令牌发送到DAO的地址并返回结果。
然后,忽略返回的结果——我们只想知道它何时完成——最后将代币的所有权转移到DAO的地址,返回数据但不丢弃它。
运行truffle migrate --reset
现在应该产生这样的输出:
前端
前端是一个常规的静态HTML页面,其中包含一些JavaScript用于与区块链和一些CSS进行通信以使页面变得不那么难看。
让我们在子文件夹public
创建一个文件index.html
,并为其提供以下内容:
"en">
The Neverending Story
"utf-8"/>
"viewport" content="width=device-width, initial-scale=1" />
"description" content="The Neverending Story is an community curated and moderated Ethereum dapp-story" >
"stylesheet" href="assets/css/main.css" />
"grid-container">
"header container">
The Neverending Story
A story on the Ethereum blockchain, community curated and moderated through a Decentralized Autonomous Organization (DAO)
"content container">
"intro">
Chapter 0
"intro">It's a rainy night in central London.
This is an example submission. A proposal for its deletion has been submitted.
0xbE2B28F870336B4eAA0aCc73cE02757fcC428dC9
This is a long submission. It has over 244 characters, just we can see what it looks like when rendered in the UI. We need to make sure it doesn't break anything and the layout also needs to be maintained, not clashing with actions/buttons etc.
"submission-submitter">0xbE2B28F870336B4eAA0aCc73cE02757fc C428dC9
"submission-actions">
"delete">
"submission">
"submission-body">This is an example submission. A proposal for its deletion has been submitted but is looking like it'll be rejected.
0xbE2B28F870336B4eAA0aCc73cE02757fcC428dC9
复制代码
注意:这是一个非常基本的框架,仅用于演示集成。请不要把这当成最终产品!
你可能缺少web3
文件夹中的dist
文件夹。该软件仍处于测试阶段,因此仍有可能出现轻微漏洞。要解决此问题并使用dist
文件夹,请运行npm install ethereum/web3.js --save
。
对于CSS,让我们在public/assets/css/main.css
一些基本内容:
@supports (grid-area: auto) {
.grid-container{
display: grid;
grid-template-columns: 6fr 5fr 4fr;
grid-template-rows: 10rem ;
grid-column-gap: 0.5rem;
grid-row-gap: 0.5rem;
justify-items: stretch;
align-items: stretch;
grid-template-areas:
"header header information"
"content events information" ;
height: 100vh;
}
.events {
grid-area: events;
}
.content {
grid-area: content;
}
.information {
grid-area: information;
}
.header {
grid-area: header;
text-align: center;
}
.container {
border: 1px solid black;
padding: 15px;
overflow-y: scroll;
}
p {
margin: 0;
}
}
body {
padding: 0;
margin: 0;
font-family: sans-serif;
}
复制代码
然后作为JS,我们将在public/assets/js/app.js
:
var Web3 = require('web3' );
var web3 = new Web3(web3.currentProvider);
console.log(web3);
复制代码
这里发生了什么?
既然我们假设所有用户都安装了MetaMask,并且MetaMask将自己的Web3实例注入到任何访问过的网页的DOM中,我们基本上可以访问我们网站上MetaMask的wallet provider
。实际上,如果我们在页面打开时登录MetaMask,我们将在控制台中看到:
注意MetamaskInpageProvider
是如何激活的。实际上,如果我们在控制台中键入web3.eth.accounts
,我们将通过MetaMask访问的所有帐户都将打印出来:
但是,这个特殊帐户默认添加到我自己的个人Metamask中,因此余额为0eth。它不是我们运行的Ganache或PoA链的一部分:
请注意,如果要求我们的MetaMask活动帐户的余额产生0,同时要求我们的一个私有区块链帐户的余额产生100以太(在我的情况下它是Ganache,所以所有帐户都用100以太初始化)。
关于语法
你会注意到这些调用的语法看起来有点奇怪:
web3.eth.getBalance("0x35d4dCDdB728CeBF80F748be65bf84C776B0Fbaf" , function (err, res){console.log(JSON.stringify(res));});
复制代码
为了读取区块链数据,大多数MetaMask用户不会在本地运行节点,而是从Infura或其他远程节点请求它。因此,我们实际上可以依靠回调。因此,通常不支持同步方法。相反,一切都是通过promise或回调来完成的——就像本文开头的部署步骤一样。这是否意味着你需要非常熟悉为以太坊开发JS的promise?不,这意味着以下内容。在DOM中进行JS调用时......
总是提供一个回调函数作为你正在调用的函数的最后一个参数。
假设它的返回值是双重的:第一个error
,然后是result
。
所以,基本上,只需考虑延迟响应就可以了。当节点响应数据时,你定义为回调函数的函数将由JavaScript调用。是的,这意味着你不能指望你的代码在编写时逐行执行!
有关promises,回调和所有async jazz
的更多信息,请参阅此文章。
帐户信息
如果我们打开上面提到的网站骨架,我们得到这样的东西:
让我们用真实数据填充关于帐户信息的最右侧列。
session
当用户未登录其MetaMask扩展名时,帐户列表将为空。如果甚至没有安装MetaMask,则提供程序将为空(未定义)。当他们登录MetaMask时,接口将可用并提供帐户信息以及与连接的以太坊节点(live或Ganache或其他)的交互。
提示:要进行测试,你可以通过单击右上角的头像图标然后选择注销来注销MetaMask。如果用户界面看起来不像下面的屏幕截图,你可能需要通过打开菜单并单击“试用Beta”来激活Beta用户界面。
首先,如果用户已注销,请将该状态列的所有内容替换为用户的消息:
"information container">
"logged out">
You seem to be logged out of MetaMask or MetaMask isn't installed. Please log into MetaMask - to learn more,
see
this tutorial .
复制代码
处理它的JS看起来像这样(在public/assets/js/main.js
):
var loggedIn;
(function () {
loggedIn = set LoggedIn(web3.currentProvider !== undefined && web3.eth.accounts.length > 0);
})();
function set LoggedIn(isLoggedIn) {
let loggedInEl = document.querySelector('div.logged.in' );
let loggedOutEl = document.querySelector('div.logged.out' );
if (isLoggedIn) {
loggedInEl.style.display = "block" ;
loggedOutEl.style.display = "none" ;
} else {
loggedInEl.style.display = "none" ;
loggedOutEl.style.display = "block" ;
}
return isLoggedIn;
}
复制代码
第一部分——(function () { -
包含一旦网站加载就要执行的逻辑。因此,当页面准备就绪时,内部的任何内容都会立即执行。调用单个函数setLoggedIn
并将条件传递给它条件是:
设置web3对象的currentProvider(即网站中存在web3客户端)。
可用的帐户数量非零,即可通过此Web3提供商使用帐户。 换句话说,我们已登录至少一个帐户。
如果这些条件一起评估为true
,则setLoggedIn
函数使“Logged out”消息不可见,并且“Logged In”消息可见。
所有这些都具有能够使用任何其他web3提供商的额外优势。如果最终出现MetaMask替代方案,它将立即与此代码兼容,因为我们并未明确期望任何地方的MetaMask。
帐户头像
因为以太坊钱包的每个私钥都是唯一的,所以它可用于生成独特的图像。这是你在MetaMask的右上角或使用MyEtherWallet时看到的彩色化身,尽管Mist,MyEtherWallet和MetaMask都使用不同的方法。让我们为登录用户生成一个并显示它。
Mist中的图标是使用Blockies库生成的——是自定义的,因为原始文件具有损坏的随机数生成器,并且可以为不同的键生成相同的图像。因此,要安装此文件,请将此文件下载到assets/js
文件夹中。然后,在index.html
我们在main.js
之前包含它:
复制代码
我们还应该升级logged.in
容器:
"logged in" style=
"display: none" >
You are logged in !
"avatar">
复制代码
在main.js,我们启动该功能。
if (isLoggedIn) {
loggedInEl.style.display = "block" ;
loggedOutEl.style.display = "none" ;
var icon = blockies.create({ // All options are optional
seed: web3.eth.accounts[0], // seed used to generate icon data, default: random
size: 20, // width/height of the icon in blocks, default: 8
scale: 8, // width/height of each block in pixels, default: 4
});
document.querySelector("div.avatar" ).appendChild(icon);
复制代码
因此,我们升级JS代码的登录部分以生成图标并将其粘贴到头像部分。我们应该在渲染之前将它与CSS稍微对齐:
div.avatar { width: 100%; text-align: center; margin: 10px 0; }
复制代码
现在,如果我们在登录MetaMask时刷新页面,我们应该会看到生成的头像图标。
帐户余额
现在让我们输出一些帐户余额信息。
我们拥有一系列只读功能,我们专门为此目的而开发。所以让我们查询区块链并询问一些信息。为此,我们需要通过以下步骤调用智能合约功能。
1.ABI
获取我们正在调用的函数的合约的ABI。ABI包含函数签名,因此我们的JS代码知道如何调用它们。在此处了解有关ABI的更多信息。
你可以通过在编译后打开项目文件夹中的build/TNSToken.json
和build/StoryDao.json
文件并仅选择abi
部分来获取TNS代币和StoryDAO的ABI([
和]
方括号之间的部分):
我们将这个ABI放在我们的JavaScript代码的顶部,进入main.js
如下所示:
请注意,上面的屏幕截图显示了我的代码编辑器(Microsoft Visual Code)折叠的缩写插入。如果你查看行号,你会注意到令牌的ABI是400行代码,而DAO的ABI是另外1000行,所以将它粘贴到本文中是没有意义的。
2.实例化代币
if (loggedIn) {
var token = TNSToken.at('0x3134bcded93e810e1025ee814e87eff252cff422' );
var story = StoryDao.at('0x729400828808bc907f68d9ffdeb317c23d2034d5' );
token.balanceOf(web3.eth.accounts[0], function (error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function (error, result) {console.log(JSON.stringify(result))});
//...
复制代码
我们使用Truffle给我们的地址调用每个合约,并分别为每个token
和story
创建一个实例。然后,我们简单地调用函数(与以前一样异步)。控制台给我们两个零,因为MetaMask中的帐户有0个代币,因为现在故事story中有0个提交。
3.读取和输出数据
最后,我们可以使用我们提供的信息填充用户的个人资料数据。
让我们更新我们的JavaScript:
var loggedIn;
(function () {
loggedIn = set LoggedIn(web3.currentProvider !== undefined && web3.eth.accounts.length > 0);
if (loggedIn) {
var token = TNSToken.at('0x3134bcded93e810e1025ee814e87eff252cff422' );
var story = StoryDao.at('0x729400828808bc907f68d9ffdeb317c23d2034d5' );
token.balanceOf(web3.eth.accounts[0], function (error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function (error, result) {console.log(JSON.stringify(result))});
read UserStats().then(User => renderUserInfo(User));
}
})();
async function read UserStats(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
var User = {
numberOfSubmissions: await getSubmissionsCountForUser(address),
numberOfDeletions: await getDeletionsCountForUser(address),
isWhitelisted: await isWhitelisted(address),
isBlacklisted: await isBlacklisted(address),
numberOfProposals: await getProposalCountForUser(address),
numberOfVotes: await getVotesCountForUser(address)
}
return User;
}
function renderUserInfo(User) {
console.log(User);
document.querySelector('#user_submissions' ).innerHTML = User.numberOfSubmissions;
document.querySelector('#user_deletions' ).innerHTML = User.numberOfDeletions;
document.querySelector('#user_proposals' ).innerHTML = User.numberOfProposals;
document.querySelector('#user_votes' ).innerHTML = User.numberOfVotes;
document.querySelector('dd.user_blacklisted' ).style.display = User.isBlacklisted ? 'inline-block' : 'none' ;
document.querySelector('dt.user_blacklisted' ).style.display = User.isBlacklisted ? 'inline-block' : 'none' ;
document.querySelector('dt.user_whitelisted' ).style.display = User.isWhitelisted ? 'inline-block' : 'none' ;
document.querySelector('dd.user_whitelisted' ).style.display = User.isWhitelisted ? 'inline-block' : 'none' ;
}
async function getSubmissionsCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getDeletionsCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getProposalCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getVotesCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function isWhitelisted(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(false );
});
}
async function isBlacklisted(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(false );
});
}
复制代码
让我们更改个人资料信息部分:
"logged in" style=
"display: none" >
You are logged in !
"avatar">
Submissions
"user_submissions">
Proposals
"user_proposals">
Votes
"user_votes">
Deletions
"user_deletions">
"user_whitelisted">Whitelisted
"user_whitelisted">Yes
"user_blacklisted">Blacklisted
"user_blacklisted">Yes
复制代码
你会注意到我们在获取数据时使用了promises,即使我们的函数当前只是模拟函数:它们会立即返回平面数据。这是因为每个函数都需要不同的时间来获取我们要求它获取的数据,因此我们将在填充User
对象之前等待它们完成,然后将其传递给render
函数,该函数更新了屏幕。
如果您对JS承诺不熟悉并希望了解更多信息,请参阅此帖子。
现在,我们所有的功能都是嘲笑; 我们需要先做一些写操作才能阅读。 但首先我们需要准备好注意那些写作的发生!
监听事件
为了能够跟踪合约发出的事件,我们需要监听它们——否则我们将所有这些emit
语句都放入代码中。我们构建的模拟UI的中间部分用于保存这些事件。
以下是我们如何监听区块链发出的事件:
// Events
var WhitelistedEvent = story.Whitelisted(function (error, result) {
if (!error) {
console.log(result);
}
})
复制代码
这里我们在StoryDao合约的story
实例上调用Whitelisted
函数,并将回调传递给它。每当触发此给定事件时,将自动调用此回调。因此,当用户被列入白名单时,代码将自动将该事件的输出记录到控制台。
但是,这只会获取网络挖掘的最后一个块的最后一个事件。因此,如果从第1块到第10块触发了几个白名单事件,它只会向我们展示第10块中的那些事件,如果有的话。更好的方法是使用这种方法:
story.Whitelisted({}, { fromBlock: 0, toBlock: 'latest' }).get((error, eventResult) => {
if (error) {
console.log('Error in myEvent event handler: ' + error);
} else {
// eventResult contains list of events!
console.log('Event: ' + JSON.stringify(eventResult[0].args));
}
});
复制代码
注意:将上面的内容放在JS文件底部的一个单独的部分,一个专门用于事件。
在这里,我们使用get
函数,它允许我们定义从中获取事件的块范围。我们使用0到最新,这意味着我们可以获取此类型的所有事件。但是这增加了与上述监听方法发生冲突的可能。监听方法输出最后一个块的事件,get
方法输出所有这些事件。我们需要一种方法来使JS忽略双重事件。不要写那些你已经从历史中获取的东西。我们会进一步做到这一点,但就目前而言,让我们来处理白名单。
帐户白名单
最后,让我们进行一些写操作。
第一个也是最简单的一个是白名单。请记住,要获得白名单,帐户需要向DAO的地址发送至少0.01以太。你将在部署时获得此地址。如果你的Ganache/PoA链在本课程的各个部分之间重新启动,那没关系,只需使用truffle migrate --reset
重新运行,你就可以获得代币和DAO的新地址。在我的例子中,DAO的地址是0x729400828808bc907f68d9ffdeb317c23d2034d5
,我的代币是0x3134bcded93e810e1025ee814e87eff252cff422
。
设置完所有内容后,让我们尝试向DAO地址发送一定数量的以太。让我们尝试0.05以太只是为了好玩,所以我们可以看看DAO是否为我们提供额外的计算代币,以支付超额费用。
注意:不要忘记自定义gas量——只需在21000限制之上再拍一个零——使用标记为红色的图标。为什么这有必要?因为由简单的ether发送(回调函数)触发的函数执行超过21000的额外逻辑,这对于简单发送就足够了。所以我们需要达到极限。不要担心:超出此限制的任何内容都会立即退款。有关gas如何工作的入门读物,请参见[www.sitepoint.com/ethereum-tr…]。
在交易确认后(你将在MetaMask中将其视为“已确认”),我们可以在MetaMask帐户中检查代币金额。我们首先需要将自定义代币添加到MetaMask中,以便跟踪它们。根据下面的动画,过程如下:选择MetaMask菜单,向下滚动到Add Tokens
,选择Custom Token
,粘贴Truffle
在迁移时给你的代币地址,点击Next
,查看余额是否为ok,然后选择添加Add Tokens
。
对于0.05 eth,我们应该有400k令牌,我们这样做。
但是这个事件怎么样?我们收到了这个白名单的通知吗?我们来看看控制台吧。
实际上,完整的数据集就在那里——发出事件的地址,块数和挖掘它的hash,等等。其中包括args
对象,它告诉我们事件数据:addr
是被列入白名单的地址,状态是它是添加到白名单还是从中删除。成功!
如果我们现在刷新页面,则事件再次出现在控制台中。但是怎么样?我们没有将任何新人列入白名单。为什么事件会发生警告?EVM中的事件是它们不像JavaScript那样是一次性的事情。当然,它们包含任意数据并仅用作输出,但它们的输出永远在区块链中注册,因为导致它们的交易也永久地在区块链中注册。因此事件将在发出之后保留,这使我们不必将它们存储在某处并在页面刷新时调用它们!
现在让我们将其添加到UI中的事件屏幕!编辑JavaScript文件的Events
部分,如下所示:
// Events
var highestBlock = 0;
var WhitelistedEvent = story.Whitelisted({}, { fromBlock: 0, toBlock: "latest" });
WhitelistedEvent.get((error, eventResult) => {
if (error) {
console.log('Error in Whitelisted event handler: ' + error);
} else {
console.log(eventResult);
let len = eventResult.length;
for (let i = 0; i < len; i++) {
console.log(eventResult[i]);
highestBlock = highestBlock < eventResult[i].blockNumber ? eventResult[i].blockNumber : highestBlock;
print Event("Whitelisted" , eventResult[i]);
}
}
});
WhitelistedEvent.watch(function (error, result) {
if (!error && result.blockNumber > highestBlock) {
print Event("Whitelisted" , result);
}
});
function print Event(type , object) {
switch (type ) {
case "Whitelisted" :
let el;
if (object.args.status === true ) {
el = "Whitelisted address " + object.args.addr +"" ;
} else {
el = "Removed address " + object.args.addr +" from whitelist!" ;
}
document.querySelector("ul.eventlist" ).innerHTML += el;
break ;
default:
break ;
}
}
复制代码
哇,变得很快,是吧?不用担心,我们会澄清。
highestBlock
变量将记住从历史记录中获取的最新块。我们创建了一个事件的实例,并为它附加了两个监听器。一个是get
,它从历史记录中获取所有事件并记住最新的块。另一个是watch
,watch
事件“实时”并在最近一个块中出现新事件时触发。只有当刚刚进入的块大于我们记忆中最高的块时,观察者才会触发,确保只有新事件被附加到事件列表中。
我们还添加了一个printEvent
函数执行打印事件的操作; 我们也可以将它重复用于其他类型的事件!
如果我们现在测试它,确实,我们可以很好地打印出来。
现在尝试自己做这个,我们的故事Story可以发出的所有其他事件!看看你是否可以弄清楚如何一次处理它们,而不必为每个都写出这个逻辑。(提示:在数组中定义它们的名称,然后遍历这些名称并动态注册事件!)
手动检查
你还可以通过在MyEtherWallet中打开并调用其whitelist
函数来手动检查StoryBAO的白名单和所有其他公共参数。
你会注意到,如果我们检查刚刚发送白名单金额的帐户,我们将获得true
回复,表明此帐户确实存在于whitelist
映射中。
在将其添加到Web UI之前,使用此相同的功能菜单来试验其他功能。
提交参赛作品
最后,让我们从UI进行正确的写函数调用。这一次,我们将在故事Story中提交一个条目。首先,我们需要清除我们在开始时放在那里的示例条目。编辑HTML到这个:
"content container">
"intro">
Chapter 0
"intro">It's a rainy night in central London.
Submit
...
复制代码
还有一些基本的CSS:
.submission_input textarea {
width: 100%;
}
复制代码
我们添加了一个非常简单的textarea,用户可以通过它提交新条目。
我们现在来做JS部分吧。
首先,让我们准备通过添加一个新事件并修改我们的printEvent
函数来接受这个事件。我们还可以对整个事件部分进行一些重构,以使其更具可重用性。
// Events
var highestBlock = 0;
var WhitelistedEvent = story.Whitelisted({}, { fromBlock: 0, toBlock: "latest" });
var SubmissionCreatedEvent = story.SubmissionCreated({}, { fromBlock: 0, toBlock: "latest" });
var events = [WhitelistedEvent, SubmissionCreatedEvent];
for (let i = 0; i < events.length; i++) {
events[i].get(history Callback);
events[i].watch(watchCallback);
}
function watchCallback(error, result) {
if (!error && result.blockNumber > highestBlock) {
print Event(result.event, result);
}
}
function history Callback(error, eventResult) {
if (error) {
console.log('Error in event handler: ' + error);
} else {
console.log(eventResult);
let len = eventResult.length;
for (let i = 0; i < len; i++) {
console.log(eventResult[i]);
highestBlock = highestBlock < eventResult[i].blockNumber ? eventResult[i].blockNumber : highestBlock;
print Event(eventResult[i].event, eventResult[i]);
}
}
}
function print Event(type , object) {
let el;
switch (type ) {
case "Whitelisted" :
if (object.args.status === true ) {
el = "Whitelisted address " + object.args.addr +"" ;
} else {
el = "Removed address " + object.args.addr +" from whitelist!" ;
}
document.querySelector("ul.eventlist" ).innerHTML += el;
break ;
case "SubmissionCreated" :
el = "User " + object.args.submitter + " created a" + ((object.args.image) ? "n image" : " text" ) +" entry: #" + object.args.index + " of content " + object.args.content+"" ;
document.querySelector("ul.eventlist" ).innerHTML += el;
break ;
default:
break ;
}
}
复制代码
现在我们需要做的就是添加一个全新的事件来实例化它,然后为它定义一个case。
接下来,让我们提交。
document.getElementById("submission-body-btn" ).addEventListener("click" , function (e) {
if (!loggedIn) {
return false ;
}
var text = document.getElementById("submission-body-input" ).value;
text = web3.toHex(text);
story.createSubmission(text, false , {value: 0, gas: 400000}, function (error, result) {
refreshSubmissions();
});
});
function refreshSubmissions () {
story.getAllSubmissionHashes(function (error, result){
console.log(result);
});
}
复制代码
在这里,我们向提交表单添加一个事件监听器,一旦提交,首先拒绝所有用户未登录的内容,然后抓取内容并将其转换为十六进制格式(这是我们需要将值存储为bytes
)。
最后,它通过调用createSubmission
函数并提供两个参数来创建交易:条目的文本和false标记(意思即不是图像)。第三个参数是交易设置:值表示要发送多少以太,而gas表示你想要默认的gas限制量。这可以在客户端(MetaMask)中手动更改,但这是一个很好的起点,以确保我们不会遇到限制。最后一个参数是我们现在已经习惯的回调函数,这个回调函数将调用一个刷新函数来加载故事Story的所有提交。目前,此刷新功能仅加载故事story哈希并将它们放入控制台,以便我们检查一切是否正常。
注意:以太量为0,因为第一个条目是免费的。进一步的条目将需要添加以太币。我们将动态计算留给你当作业。提示:为此目的,我们的DAO中有一个calculateSubmissionFee
函数。
此时,我们需要在JS的顶部更改一些在页面加载时自动执行的函数:
if (loggedIn) {
token.balanceOf(web3.eth.accounts[0], function (error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function (error, result) {console.log(JSON.stringify(result))});
web3.eth.defaultAccount = web3.eth.accounts[0]; // CHANGE
read UserStats().then(User => renderUserInfo(User));
refreshSubmissions(); // CHANGE
} else {
document.getElementById("submission-body-btn" ).disabled = "disabled" ;
}
复制代码
更改标记为//CHANGE
:第一个允许我们设置执行交易的默认帐户。这可能会在未来的Web3
版本中默认使用。第二个刷新页面加载时提交的内容,因此我们在网站打开时获得一个完整的故事story。
如果你现在尝试提交条目,MetaMask应在你单击“提交”后立即打开,并要求你确认提交。
你还应该在事件部分中看到事件打印出来。
控制台应该回显这个新条目的哈希值。
注意:MetaMask目前在私有网络和nonce
方面存在问题。它在这里描述并将很快修复,但如果nonce
在提交条目时在JavaScript控制台中收到错误,那么目前的权宜之计解决方案是重新安装MetaMask(禁用和启用将不起作用)。请记住首先备份你的种子SEED :你需要它来重新导入你的MetaMask帐户!
最后,让我们获取这些条目并显示它们。让我们从一些CSS开始:
.content-submissions .submission-submitter { font-size: small; }
复制代码
现在让我们更新一下这个refreshSubmissions
功能:
复制代码
我们浏览所有提交内容,获取它们的哈希值,获取每个哈希值,然后在屏幕上输出。如果提交者与登录用户相同,则打印“你”而不是地址。
让我们添加另一个条目进行测试。
结论
在这一部分中,我们为DApp开发了基本前端的开端。
由于开发完整的前端应用程序也可以成为它自己的一个过程,我们将作为家庭作业留给你进一步的发展。只需调用所演示的函数,将它们绑定到常规JavaScript流程中(通过像VueJS这样的框架或普通的旧jQuery或像我们上面所做的原生JS)并将它们绑定在一起。它实际上就像与标准服务器API交谈。如果你遇到困难,请查看代码的项目仓库!
可以执行的其他升级:
检测web3提供程序何时更改或可用帐户数何时更改,指示登录或注销事件并自动重新加载页面。
除非用户已登录,否则将阻止呈现提交表单。
防止呈现投票和删除按钮,除非用户至少有1个代币等。
让人们提交并呈现Markdown!
按时间(块号)订购事件,而不是按类型订购!
使事件更漂亮,更可读:不是显示十六进制内容,而是将其翻译为ASCII并截断为30个左右的字符。
使用像VueJS这样的适当的JS框架来从项目中获得一些可重用性并获得更好的结构化代码。
在下一部分和最后一部分中,我们将专注于将我们的项目部署到实时互联网。敬请关注!
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:
java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
你可能感兴趣的:(ui,区块链,java)
Ajax:万字总结黑马笔记,学懂Ajax看这一篇就够了
做一只猫
前端 ajax javascript 前端
一、了解AjaxAjax的全称是AsynchronousJavascriptAndXML(异步JavaScript和XML)通俗的理解:在网页中利用XMLHttpRequest对象和服务器进行数据交互的方式,就是Ajax如:用户名检测:注册用户时,通过ajax的形式,动态检测用户名是否被占用搜索提示:当输入搜索关键字时,通过ajax的形式,动态加载搜索提示列表数据分页显示:当点击页码值的时候,通过
Python - 安装 Python、pip、virtualenv
伊织产研
# Python python pip 源
文章目录一、PythonUbuntu安装Python问题创建虚拟环境一些使用问题Nomodulenamed'_sqlite3'二、pip(管理第三方库)1、查看pip版本2、安装pip3、升级pip4、卸载pip5、查看已安装的pkg6、第三方库site-packages地址:7、更新8、切换源9、根据requirements.txt安装其它三、virtualenv(管理多个env)1、安装2、查
MongoDB成为最好NoSQL数据库的原因是什么?思维导图 代码示例(java 架构)
用心去追梦
mongodb nosql java
MongoDB成为最受欢迎的NoSQL数据库之一,有多个因素促成了其成功和广泛采用。以下是从不同角度分析MongoDB成为最好NoSQL数据库的原因:MongoDB成为最好NoSQL数据库的原因文档型数据模型灵活模式:支持动态模式,无需预定义固定的表结构,易于适应快速变化的需求。嵌套结构:允许复杂的数据类型如数组、嵌入式文档,简化了数据建模。高性能与可扩展性读写性能:通过索引优化、内存映射文件等技
大侠,你真的了解JS中的toString&toLocaleString方法吗?
不做超级小白
web前端 javascript 前端 开发语言
toString()与toLocaleString()的区别:你需要了解的JavaScript字符串化方法在JavaScript中,toString()和toLocaleString()都是对象转换为字符串的常用方法。虽然它们的功能看似相似,但实际上它们有着不同的用途和行为。本文将详细解析这两者的区别,帮助开发者更好地理解并选择适合的字符串化方法。1.toString()方法:目的:toStrin
Jupyter Notebook 与 PyTorch 配置教程
如若123
jupyter pytorch ide
JupyterNotebook与PyTorch配置教程安装build-essential:sudoaptinstallbuild-essential安装编译软件所需的基本工具。安装Python3.8:sudoaptinstallpython3.8如果未安装Python3.8,执行此命令进行安装。下载Miniconda:wgethttps://repo.anaconda.com/miniconda/
Java实现蓝桥杯正则问题
【繁华】
题目描述考虑一种简单的正则表达式:只由x()|组成的正则表达式。小明想求出这个正则表达式能接受的最长字符串的长度。例如((xx|xxx)x|(x|xx))xx能接受的最长字符串是:xxxxxx,长度是6。输入一个由x()|组成的正则表达式。输入长度不超过100,保证合法。输出这个正则表达式能接受的最长字符串的长度。样例输入((xx|xxx)x|(x|xx))xx样例输出6PS:本题栈的基础应用思路
C语言基础------练习3
Oracle_666
c语言
输入带空格的字符串,求单词个数__ueooe_eui_sjje__---->3syue__jdjd____die_---->3shuue__dju__kk---->3代码实现结果:代码解析:/*定义字符数组charinput[100];//用于存储输入的字符串定义一个字符数组input用于存储输入的字符串。初始化变量intword_count=0;intin_word=0;//标志位,表示当前是否
docker builds not a command
Zack Snyder
docker eureka 容器 运维 云原生
我整理的一些关于【Docker】的项目学习资料(附讲解~~)和大家一起分享、学习一下:https://d.51cto.com/xltfov理解Docker和构建过程中的命令问题:处理“dockerbuildsnotacommand”错误Docker是一种流行的容器化技术,它允许开发者将应用程序及其所有依赖项打包到一个标准化的单元(容器)中,确保应用在任何环境中都能一致地运行。然而,在使用Docke
转帖-在Eclipse中开发JSF
ren_z_q
JSF Eclipse Bean JSP Oracle
(转自http://www.blogjava.net/gaofeng/articles/127842.html作者:Java.net)Eclipse3.3刚刚发布,正在学习JSF,于是使用Eclipse3.3做了一个JSF的Demo,很简单,主要是页面的跳转、组件和Bean的绑定等基础...1、工具准备:Eclipse3.3WTP2.0(最好下载一个all-in-one的版本..省的麻烦)...依
TypeScript and Serverless Memory Issue
magic_dreamer
Summary Scripts serverless javascript webpack ViewUI
TypeScriptandServerlessMemoryIssueChangethefiletoaddthefollowcodesatthetop/opt/node/lib/node_modules/serverless/bin/serverless#!/usr/bin/envnode--max-old-space-size=3000--trace-gc-verboseJavaScripthea
paddleseg推理预测文件解析predict.py
weightOneMillion
图像分割 每天一篇PaddleSeg 学习 python 人工智能
1预测命令格式predict.py脚本是专门用来可视化预测案例的,命令格式如下所示:pythonpredict.py\--configconfigs/quick_start/bisenet_optic_disc_512x512_1k.yml\--model_pathoutput/iter_1000/model.pdparams\--ima
自动化测试--概念篇
.比奇堡派大星.
软件测试 自动化测试 selenium
博主主页:码农派大星.数据结构专栏:Java数据结构数据库专栏:数据库JavaEE专栏:JavaEE软件测试专栏:软件测试关注博主带你了解更多知识目录1.⾃动化1.1自动化概念1.1.1回归测试1.2⾃动化分类接⼝⾃动化UI⾃动化1.3⾃动化测试⾦字塔2.web⾃动化测试安装驱动管理3.Selenium安装selenium库使⽤selenium编写代码selenium+驱动+浏览器的⼯作原理1.⾃
python之函数的定义
徐jiankang
python基础 日常总结 python 开发语言
博主简介:原互联网大厂tencent员工,网安巨头Venustech员工,阿里云开发社区专家博主,微信公众号java基础笔记优质创作者,csdn优质创作博主,创业者,知识共享者,欢迎关注,点赞,收藏。目录一、背景二、函数的定义三、参考四、总结一、背景 实际开发过程中,经常会遇到很多完全相同或者非常相似的操作,这时,可以将实现类似操作的代码封装为函数,然后在需要的地方调用该函数。这样不仅可以实现代
Serverless Plugin Optimize 使用指南
舒林艾Natalie
ServerlessPluginOptimize使用指南serverless-plugin-optimize⛔️DEPRECATED⛔️BundlewithBrowserify,transpileandminifywithBabelautomaticallytoyourNodeJSruntimecompatibleJavaScript项目地址:https://gitcode.com/gh_mirr
项目推荐:Serverless优化插件 —— 提升您的云函数性能与效率
井队湛Heath
项目推荐:Serverless优化插件——提升您的云函数性能与效率serverless-plugin-optimize⛔️DEPRECATED⛔️BundlewithBrowserify,transpileandminifywithBabelautomaticallytoyourNodeJSruntimecompatibleJavaScript项目地址:https://gitcode.com/gh
2021年Javascript最常见的面试题以及答案
2401_86401365
javascript 原型模式 开发语言
区别:||和原数据是否指向同一个对象|第一层数据为基本数据类型|原数据中包含的子对象||—|—|—|—||浅拷贝|否|不会使原数据一起改变|会使原数据一起改变||深拷贝|否|不会使原数据一起改变|不会使原数据一起改变|点击对Javscript中浅拷贝和深拷贝的探索和详解查看详解项目中实现深浅拷贝常用的方法有哪些?===========================================
【Java学习】从0到1掌握行为抽象与Lambda表达式:分区的深度解析与实战指南
墨瑾轩
一起学学Java【二】 java 学习 开发语言
关注墨瑾轩,带你探索编程的奥秘!超萌技术攻略,轻松晋级编程高手技术宝库已备好,就等你来挖掘订阅墨瑾轩,智趣学习不孤单即刻启航,编程之旅更有趣从0到1掌握行为抽象与Lambda表达式:分区的深度解析与实战指南!引言在现代编程中,行为抽象和Lambda表达式是提高代码可读性和灵活性的重要工具。特别是在Java8引入的流(Stream)API和分区功能,更是让处理集合数据变得简单而优雅。今天,我们就一起
咱们继续学Java——高级篇 第二百五十五篇:之Java进阶之本地方法:Windows注册表访问代码的终极解读
一杯年华@编程空间
咱们继续学java高级篇 maven java-ee spring boot spring cloud hibernate tomcat
咱们继续学Java——高级篇第二百五十五篇:之Java进阶之本地方法:Windows注册表访问代码的终极解读在Java学习的道路上,我们不断追求代码理解的深度,每一次对复杂代码的终极解读都是成长的重要里程碑。我写这篇博客的目的,就是希望与大家一同深入剖析Java本地方法中访问Windows注册表的剩余关键代码,助力大家在Java与其他语言交互编程领域掌握最核心的技能。今天,我们将详细解读Win32
java语言中“导包”的解释
喵果森森
java编程学习日志 java jvm servlet
在java编程过程中,常常使用一种功能,叫“导包”。关键字为import例如importjava.util.Scanner;importjava.util.Random;什么是导包?导包即导入包,通过import关键字将他人完善的代码导入自己的代码中。“他人完善的代码”是被封装成类和包的形式,导入包,并不会将他人的代码插入自己的代码里,只需将其对象实例化后使用即可。什么人写的代码都可以导入吗?Ja
23种设计模式-桥接(Bridge)设计模式
萨达大
软考中级-软件设计师 设计模式 java C++ 桥接模式 结构型设计模式 软考 软件设计师
文章目录一.什么是桥接设计模式?二.桥接模式的特点三.桥接模式的结构四.桥接模式的优缺点五.桥接模式的C++实现六.桥接模式的Java实现七.代码解析八.总结类图:桥接设计模式类图一.什么是桥接设计模式? 桥接模式(BridgePattern)是一种结构型设计模式,用于将抽象部分与实现部分分离,使它们可以独立地变化。通过桥接模式,抽象层和实现层之间的耦合度被降低,从而使系统具有更好的灵活性和可扩展
实际开发中的有趣bug:“undefined“ is not valid JSON SyntaxError: “undefined“ is not valid JSON。
我爱加班、、
vue项目实际开发中的bug vuex bug json 前端 javascript vue.js ecmascript ajax
bug解读:指出在尝试解析或序列化JSON数据时遇到了问题。JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,它要求数据必须是有效的JSON格式。在JavaScript中,undefined是一个特殊的值,表示变量没有被赋值,它不是一个有效的JSON值。场景:在后台项目,用户登录后,通过用户的菜单权限渲染侧边栏菜单,做了持久化存储。当登出后,在登录页刷新页面,
Java 和 JavaScript 的区别
大乔乔布斯
java javascript 开发语言
尽管名字相似,JavaScript的名字中带有“Java”,确实让很多人误以为它与Java有紧密联系。但实际上,它们是完全不同的语言,只是在JavaScript的发展历史中与Java有一定的关联。1.JavaScript的诞生背景时间点:1995年,网景公司(Netscape)开发了一种轻量级的脚本语言,用于增强网页的交互性。开发者:JavaScript的发明者是布兰登·艾奇(BrendanEic
MySQL 拆分字符串函数Split
大乔乔布斯
mysql 数据库
MYSQL目前没有Hive或者Java。python这列直接split的函数,需要自己定义一个,复制代码,一键使用CREATEDEFINER=`root`@`localhost`FUNCTION`func_split_str`(xVARCHAR(255),--字符串delimVARCHAR(12),--分隔符posINT--按分隔浮拆分后的第几个结果,从1开始数)RETURNSvarchar(25
草稿随笔1
weixin_42811974
python
fromselenium.webdriver.common.byimportByfromtest_appium.page.BasePage1importBasePageimporttimefromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_condition
vue-cli项目中使用Electron
骆驼Lara
Vue vue.js electron javascript
Vue项目中使用Electron一、安装二、创建background.js三、创建preload.js文件四、修改package.json文件五、修改vue.config.js文件六、启动1本地启动2打包补充:一、安装安装electron,electron-builder,vue-cli-plugin-electron-builder,electron-devtoolsnpmi--save-dev
Vue企业开发实战——学习心得
sienn
vue.js 前端 javascript
一、Vue.js简介Vue.js是一个渐进式JavaScript框架,用于构建用户界面。它与其他大型框架的不同之处在于,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,也便于与第三方库或已有项目整合。二、环境配置安装Node.js和npm:Vue.js的开发需要使用Node.js和npm(Node包管理器)。可以从Node.js的官网下载并安装Node.js,它会自动
JVM中的STW和CMS
Modify_QmQ
# JVM jvm stw cms
STWJava中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。GC时的StoptheWorld(STW)是大家最大的敌人。但可能很多人还不清楚,除了GC,JVM下还会发生停顿现象
小识Java死锁是否会造成CPU100%?
天天向上杰
java 开发语言
死锁或者大量的死锁不一定会直接导致CPU占用率达到100%。以下是详细分析:一、死锁对CPU的影响资源占用:死锁是指两个或多个线程(或进程)在相互等待对方释放资源,导致所有涉及的线程都无法继续执行。在死锁状态下,这些线程实际上并没有进行有效的计算或处理,而是处于等待状态。CPU使用情况:虽然死锁线程本身并不消耗大量的CPU资源,但它们会阻塞系统的其他部分,导致整体性能下降。如果系统中存在大量的死锁
简识JVM栈中的程序计数器
天天向上杰
jvm
JVM(Java虚拟机)栈中的程序计数器(ProgramCounterRegister)是JVM运行时数据区域中的一个重要组成部分,以下是对其的详细解释:一、程序计数器的定义和作用定义:程序计数器是一块较小的内存区域,每个线程在创建时都会分配一个独立的程序计数器,因此它是线程私有的。作用:程序计数器的主要作用是存储当前线程正在执行的Java方法的字节码指令地址。它是一个指示器,指向方法区中该线程正
Hibernate和Spring Data JPA
打伞的木头人
什么是JavaPersistenceAPI?JavaPersistenceAPI提供了一个规范,用于将数据通过Java对象持久化、读取和管理到数据库中的关系表。JPA是JavaPersistenceAPI的简称,中文名Java持久层API,是JDK5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。Sun引入新的JPAORM规范出于两个原因:其一,简化现有JavaE
对股票分析时要注意哪些主要因素?
会飞的奇葩猪
股票 分析 云掌股吧
众所周知,对散户投资者来说,股票技术分析是应战股市的核心武器,想学好股票的技术分析一定要知道哪些是重点学习的,其实非常简单,我们只要记住三个要素:成交量、价格趋势、振荡指标。
一、成交量
大盘的成交量状态。成交量大说明市场的获利机会较多,成交量小说明市场的获利机会较少。当沪市的成交量超过150亿时是强市市场状态,运用技术找综合买点较准;
【Scala十八】视图界定与上下文界定
bit1129
scala
Context Bound,上下文界定,是Scala为隐式参数引入的一种语法糖,使得隐式转换的编码更加简洁。
隐式参数
首先引入一个泛型函数max,用于取a和b的最大值
def max[T](a: T, b: T) = {
if (a > b) a else b
}
因为T是未知类型,只有运行时才会代入真正的类型,因此调用a >
C语言的分支——Object-C程序设计阅读有感
darkblue086
apple c 框架 cocoa
自从1972年贝尔实验室Dennis Ritchie开发了C语言,C语言已经有了很多版本和实现,从Borland到microsoft还是GNU、Apple都提供了不同时代的多种选择,我们知道C语言是基于Thompson开发的B语言的,Object-C是以SmallTalk-80为基础的。和C++不同的是,Object C并不是C的超集,因为有很多特性与C是不同的。
Object-C程序设计这本书
去除浏览器对表单值的记忆
周凡杨
html 记忆 autocomplete form 浏览
&n
java的树形通讯录
g21121
java
最近用到企业通讯录,虽然以前也开发过,但是用的是jsf,拼成的树形,及其笨重和难维护。后来就想到直接生成json格式字符串,页面上也好展现。
// 首先取出每个部门的联系人
for (int i = 0; i < depList.size(); i++) {
List<Contacts> list = getContactList(depList.get(i
Nginx安装部署
510888780
nginx linux
Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源
java servelet异步处理请求
墙头上一根草
java 异步返回 servlet
servlet3.0以后支持异步处理请求,具体是使用AsyncContext ,包装httpservletRequest以及httpservletResponse具有异步的功能,
final AsyncContext ac = request.startAsync(request, response);
ac.s
我的spring学习笔记8-Spring中Bean的实例化
aijuans
Spring 3
在Spring中要实例化一个Bean有几种方法:
1、最常用的(普通方法)
<bean id="myBean" class="www.6e6.org.MyBean" />
使用这样方法,按Spring就会使用Bean的默认构造方法,也就是把没有参数的构造方法来建立Bean实例。
(有构造方法的下个文细说)
2、还
为Mysql创建最优的索引
annan211
mysql 索引
索引对于良好的性能非常关键,尤其是当数据规模越来越大的时候,索引的对性能的影响越发重要。
索引经常会被误解甚至忽略,而且经常被糟糕的设计。
索引优化应该是对查询性能优化最有效的手段了,索引能够轻易将查询性能提高几个数量级,最优的索引会比
较好的索引性能要好2个数量级。
1 索引的类型
(1) B-Tree
不出意外,这里提到的索引都是指 B-
日期函数
百合不是茶
oracle sql 日期函数 查询
ORACLE日期时间函数大全
TO_DATE格式(以时间:2007-11-02 13:45:25为例)
Year:
yy two digits 两位年 显示值:07
yyy three digits 三位年 显示值:007
线程优先级
bijian1013
java thread 多线程 java多线程
多线程运行时需要定义线程运行的先后顺序。
线程优先级是用数字表示,数字越大线程优先级越高,取值在1到10,默认优先级为5。
实例:
package com.bijian.study;
/**
* 因为在代码段当中把线程B的优先级设置高于线程A,所以运行结果先执行线程B的run()方法后再执行线程A的run()方法
* 但在实际中,JAVA的优先级不准,强烈不建议用此方法来控制执
适配器模式和代理模式的区别
bijian1013
java 设计模式
一.简介 适配器模式:适配器模式(英语:adapter pattern)有时候也称包装样式或者包装。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类别自己的接口包裹在一个已存在的类中。 &nbs
【持久化框架MyBatis3三】MyBatis3 SQL映射配置文件
bit1129
Mybatis3
SQL映射配置文件一方面类似于Hibernate的映射配置文件,通过定义实体与关系表的列之间的对应关系。另一方面使用<select>,<insert>,<delete>,<update>元素定义增删改查的SQL语句,
这些元素包含三方面内容
1. 要执行的SQL语句
2. SQL语句的入参,比如查询条件
3. SQL语句的返回结果
oracle大数据表复制备份个人经验
bitcarter
oracle 大表备份 大表数据复制
前提:
数据库仓库A(就拿oracle11g为例)中有两个用户user1和user2,现在有user1中有表ldm_table1,且表ldm_table1有数据5千万以上,ldm_table1中的数据是从其他库B(数据源)中抽取过来的,前期业务理解不够或者需求有变,数据有变动需要重新从B中抽取数据到A库表ldm_table1中。
HTTP加速器varnish安装小记
ronin47
http varnish 加速
上午共享的那个varnish安装手册,个人看了下,有点不知所云,好吧~看来还是先安装玩玩!
苦逼公司服务器没法连外网,不能用什么wget或yum命令直接下载安装,每每看到别人博客贴出的在线安装代码时,总有一股羡慕嫉妒“恨”冒了出来。。。好吧,既然没法上外网,那只能麻烦点通过下载源码来编译安装了!
Varnish 3.0.4下载地址: http://repo.varnish-cache.org/
java-73-输入一个字符串,输出该字符串中对称的子字符串的最大长度
bylijinnan
java
public class LongestSymmtricalLength {
/*
* Q75题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。
* 比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
*/
public static void main(String[] args) {
Str
学习编程的一点感想
Cb123456
编程 感想 Gis
写点感想,总结一些,也顺便激励一些自己.现在就是复习阶段,也做做项目.
本专业是GIS专业,当初觉得本专业太水,靠这个会活不下去的,所以就报了培训班。学习的时候,进入状态很慢,而且当初进去的时候,已经上到Java高级阶段了,所以.....,呵呵,之后有点感觉了,不过,还是不好好写代码,还眼高手低的,有
[能源与安全]美国与中国
comsci
能源
现在有一个局面:地球上的石油只剩下N桶,这些油只够让中国和美国这两个国家中的一个顺利过渡到宇宙时代,但是如果这两个国家为争夺这些石油而发生战争,其结果是两个国家都无法平稳过渡到宇宙时代。。。。而且在战争中,剩下的石油也会被快速消耗在战争中,结果是两败俱伤。。。
在这个大
SEMI-JOIN执行计划突然变成HASH JOIN了 的原因分析
cwqcwqmax9
oracle
甲说:
A B两个表总数据量都很大,在百万以上。
idx1 idx2字段表示是索引字段
A B 两表上都有
col1字段表示普通字段
select xxx from A
where A.idx1 between mmm and nnn
and exists (select 1 from B where B.idx2 =
SpringMVC-ajax返回值乱码解决方案
dashuaifu
Ajax springMVC response 中文乱码
SpringMVC-ajax返回值乱码解决方案
一:(自己总结,测试过可行)
ajax返回如果含有中文汉字,则使用:(如下例:)
@RequestMapping(value="/xxx.do") public @ResponseBody void getPunishReasonB
Linux系统中查看日志的常用命令
dcj3sjt126com
OS
因为在日常的工作中,出问题的时候查看日志是每个管理员的习惯,作为初学者,为了以后的需要,我今天将下面这些查看命令共享给各位
cat
tail -f
日 志 文 件 说 明
/var/log/message 系统启动后的信息和错误日志,是Red Hat Linux中最常用的日志之一
/var/log/secure 与安全相关的日志信息
/var/log/maillog 与邮件相关的日志信
[应用结构]应用
dcj3sjt126com
PHP yii2
应用主体
应用主体是管理 Yii 应用系统整体结构和生命周期的对象。 每个Yii应用系统只能包含一个应用主体,应用主体在 入口脚本中创建并能通过表达式 \Yii::$app 全局范围内访问。
补充: 当我们说"一个应用",它可能是一个应用主体对象,也可能是一个应用系统,是根据上下文来决定[译:中文为避免歧义,Application翻译为应
assertThat用法
eksliang
JUnit assertThat
junit4.0 assertThat用法
一般匹配符1、assertThat( testedNumber, allOf( greaterThan(8), lessThan(16) ) );
注释: allOf匹配符表明如果接下来的所有条件必须都成立测试才通过,相当于“与”(&&)
2、assertThat( testedNumber, anyOf( g
android点滴2
gundumw100
应用服务器 android 网络应用 OS HTC
如何让Drawable绕着中心旋转?
Animation a = new RotateAnimation(0.0f, 360.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
a.setRepeatCount(-1);
a.setDuration(1000);
如何控制Andro
超简洁的CSS下拉菜单
ini
html Web 工作 html5 css
效果体验:http://hovertree.com/texiao/css/3.htmHTML文件:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>简洁的HTML+CSS下拉菜单-HoverTree</title>
kafka consumer防止数据丢失
kane_xie
kafka offset commit
kafka最初是被LinkedIn设计用来处理log的分布式消息系统,因此它的着眼点不在数据的安全性(log偶尔丢几条无所谓),换句话说kafka并不能完全保证数据不丢失。
尽管kafka官网声称能够保证at-least-once,但如果consumer进程数小于partition_num,这个结论不一定成立。
考虑这样一个case,partiton_num=2
@Repository、@Service、@Controller 和 @Component
mhtbbx
DAO spring bean prototype
@Repository、@Service、@Controller 和 @Component 将类标识为Bean
Spring 自 2.0 版本开始,陆续引入了一些注解用于简化 Spring 的开发。@Repository注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO类上即可。同时,为了让 Spring 能够扫描类
java 多线程高并发读写控制 误区
qifeifei
java thread
先看一下下面的错误代码,对写加了synchronized控制,保证了写的安全,但是问题在哪里呢?
public class testTh7 {
private String data;
public String read(){
System.out.println(Thread.currentThread().getName() + "read data "
mongodb replica set(副本集)设置步骤
tcrct
java mongodb
网上已经有一大堆的设置步骤的了,根据我遇到的问题,整理一下,如下:
首先先去下载一个mongodb最新版,目前最新版应该是2.6
cd /usr/local/bin
wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.0.tgz
tar -zxvf mongodb-linux-x86_64-2.6.0.t
rust学习笔记
wudixiaotie
学习笔记
1.rust里绑定变量是let,默认绑定了的变量是不可更改的,所以如果想让变量可变就要加上mut。
let x = 1; let mut y = 2;
2.match 相当于erlang中的case,但是case的每一项后都是分号,但是rust的match却是逗号。
3.match 的每一项最后都要加逗号,但是最后一项不加也不会报错,所有结尾加逗号的用法都是类似。
4.每个语句结尾都要加分