首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

GraphQL前后端落地入门尝试总结

前言:首先要明白的是GraphQL是用来干嘛的也就是是解决什么问题的,其实就是解决传输数据的可定制化,减少后端api的开发量。举个例子:比如你获取一个用户列表,后端开发了一个叫getUserList接口,参数是parentId,返回的数据结构是这样的:[ { name, id , logo , address } ],后来又有个业务只要数据结构是这样的就可以了:[ { name} ],这个时候要么后端重新开发一个接口,要么就是直接用getUserList,但是造成了数据浪费和网络传输成本浪费,为了解决这个问题GraphQL就被Facebook创造了出来,即能复用getUserList也不造成各种浪费。 后端(使用的是nodejs,express) 1、安装依赖:npm install express-graphql -S ; npm install graphql -S ; 2、单独个目录做route接口,index.js统一导出,方便以后代码膨胀进行分块,如图 3、开始写具体的业务代码,拿我的base-graphql.js举例 // 也可以在不使用 GraphQL Schema Language 的情况下实现相同的 API: const { graphqlHTTP } = require('express-graphql'); const graphql = require('graphql'); const { createBaseDb, createUserDb } = require('../utils/db-util'); //创建db对象 const DBBase = createBaseDb(); // 定义 数据库对应的User类型 const UserType = new graphql.GraphQLObjectType({ name: 'User', fields: { id: { type: graphql.GraphQLInt }, version: { type: graphql.GraphQLInt }, create_date: { type: graphql.GraphQLString }, update_date: { type: graphql.GraphQLString }, update_user: { type: graphql.GraphQLInt }, name: { type: graphql.GraphQLString }, email: { type: graphql.GraphQLString }, server: { type: graphql.GraphQLString }, username: { type: graphql.GraphQLString }, password: { type: graphql.GraphQLString }, } }); const UsersType = new graphql.GraphQLList(UserType); // 定义查询对象类型,对应post查询传参: query:{user(id:"a"){id,name,age},hello(name:"charming")} const QueryType = new graphql.GraphQLObjectType({ name: 'Query', fields: { queryUser: { description: 'query user', //resolve返回的数据类型 type: UserType, // `args` 描述了 `user` 查询接受的参数 args: { id: { type: graphql.GraphQLString, } }, resolve(parentValue, args, request) { //查一个表的所有数据 let data = new Promise((resolve, reject) => { DBBase.all("select * from user", (err, res) => { if (err) { reject(err) } else { resolve(res[0]) } }) }); return data; } }, queryUsers: { description: 'query users', //resolve返回的数据类型 type: UsersType, // `args` 描述了 `user` 查询接受的参数 args: { id: { type: graphql.GraphQLString, } }, resolve(parentValue, args, request) { //查一个表的所有数据 let data = new Promise((resolve, reject) => { DBBase.all("select * from user", (err, res) => { if (err) { reject(err) } else { resolve(res) } }) }); return data; } }, //可以定义多个 hello: { description: 'a hello world demo', type: graphql.GraphQLString, args: { name: { // 这里定义参数,包括参数类型和默认值 type: graphql.GraphQLString, defaultValue: 'Brian' } }, resolve(parentValue, args, request) { // 这里演示如何获取参数,以及处理 return 'hello world ' + args.name + '!'; } } } }); // ====================================下面时修改数据================================= // 输入参数类型 const UserInputType = new graphql.GraphQLInputObjectType({ name: 'UserInput', fields: () => ({ name: { type: graphql.GraphQLString }, email: { type: graphql.GraphQLString }, username: { type: graphql.GraphQLString }, password: { type: graphql.GraphQLString }, }) }); const MutationType = new graphql.GraphQLObjectType({ name: 'Mutation', fields: { addUser: { //参数样式 mutation {addUser(one:{id:"s",name:"alice",age:12,male:false}){id,name,age}} type: graphql.GraphQLString, // `args` 描述了 `user` 查询接受的参数 args: { one: { type: UserInputType } }, resolve(parentValue, args, request) { let sqlStr = `insert into user (create_date,version,name,email,username,password) values ( '${new Date().toISOString()}', 0, '${args.one.name}', '${args.one.email}', '${args.one.username}', '${args.one.password}' )`; return new Promise((resolve, reject) => { DBBase.run('BEGIN TRANSACTION;'); DBBase.run(sqlStr, async (err, res) => { console.log("insert user ", err, res); if (err) { DBBase.run("ROLLBACK;"); reject(err) } else { //添加成功后,创建对应的用户数据库 try { await createUserDb(args.one.email); DBBase.run('COMMIT TRANSACTION;'); resolve(res) } catch (error) { console.log(error); DBBase.run("ROLLBACK;"); reject(err) } } }); }); } }, } }); const schema = new graphql.GraphQLSchema({ query: QueryType, mutation: MutationType }); module.exports = graphqlHTTP({ schema: schema, graphiql: true /* true代表需要调试 */ }) 上面注释写的也比较清楚,简单说下, 这里它提供了GraphQLObjectType来定义对象数据类型的描述,如果是数组必须使用GraphQLList再进行构建,然后你就可以用你构建的来声明你要返回的类型,但是参数类型不能使用这个,必须是GraphQLInputObjectType这种input相关的来构建。 还有resolve方法里是需要你直接return结果的,db的操作其实都是异步的,所以需要你用promise来解决 这个问题,当你的业务涉及事务等比较多db操作是async/await可能更方便,其他的用法就看官方文档吧,这里就不展开了。 例子中 我按功能来划分了模块,我综合思考了,还是觉得这样的普适性和开发难度是最好,对于一些复杂的大型项目肯定还是需要进行调整的。 好了到这里,你就可以直接用postman看看效果了 记得参数那里不要是form-data,一定选的是GraphQL(这个我猜想就是form-data等这种现有的形式来做这个可能有点别扭,所以干脆另外定了一个格式),查询参数类似这样:query {queryUsers(id:"a"){id,name,email}},修改参数类似这样:mutation {addUser(one:{name:"jack",email:"jack@qq.com",username:"jack",password:"123456"})};不用postman也可以用自带的,直接访问https://localhost:3001就可以了 。 写到这里你可能会想到现有的类似axios这种的框架不能用了,对的,必须要换成apollo-client这种支持的。所以如果是成熟项目要替换成GraphQL还是需要成本的。

优秀的个人博客,低调大师

超值的渗透测试入门基础篇

网络安全渗透测试者是什么?他们去发现测试目标的各种漏洞。正因为渗透测试者不懈努力,才让网络系统安如磐石,更好的服务广大民众。 这些年,互联网深入到了我们生活的方方面面。涉及金钱、工作等非常重要,且注重隐私的内容。因为网络安全受到重视,所以网络渗透测试作为一个新兴的行业,也得到了蓬勃的发展。 前渗透测试阶段 首先进行信息搜集,比如网站的编写语言,Web容器,服务器系统,CMS,IP,站长信息等等。然后目录探索(后台扫描),后台扫描仅仅是这个步骤中的一部分,而不是为了找到后台而扫描目录,通过目录探索可以获得很多有用的信息,比如后台管理地址等。端口扫描是使用nmap对目标的端口进行扫描,进而判断开放哪些服务,为之后打下铺垫漏洞探测,根据收集到的信息,对可能存在的漏洞进行探测,确认它是否真的存在,是否可以利用,最后将其汇总,拟成方案,就可以对已经掌握的漏洞情况进行利用,通过漏洞攻击得到shell权限 后渗透测试阶段 权限提升,既然到这步你应该已经得到一个webshell或者目标机器的一个低权限用户了,后渗透测试阶段就是利用现有的较低权限经过一些技巧、溢出漏洞得到最高权限。假如对方是一个大企业,不仅要拿下目标机器,还要利用目标机器作为跳板,对目标机器所处的内网进行攻击,可以扫一波处在同一个内网中的机器IP,然后对于我们入侵的整个过程,是要清理掉所有痕迹比如记录日志等,让目标无法察觉。最后编写报告,对于整场渗透测试所探测到的漏洞、利用的方式、进行的操作等进行汇总,形成内容完整格式简洁的报告,提交至客户。这些就是最基础的渗透思路流程。 总结 与业务有关:github泄漏、网盘泄漏、敏感路径扫描、QQ群文件泄漏。 与域名有关:域名商、DNS、CDN、WHOIS、子域名、端口、系统识别。 无恶意特征的漏洞:整数溢出,数字超出限定大小就会为负数。可以并发修改数据、网站重装、未授权访问、水平与垂直越权、密码重置。 找后台:在后台添加友联时一般会展示logo图片,如果可以填写远程URL 可以尝试和站长沟通交换友联,添加后,对方会发起一个refer为后台的请求到你的logo收集网站名简写、英文名、邮箱前缀等,在admin、manage、system、login等常见单词组合请求。找列目录漏洞,收集子域名资产及其C段端口。用引擎搜索关键字,爬虫爬取所有链接,提取出目录部分,按目录层级穷举。比如微信,QQ,钉钉,一些比较火的APP和网站。注意使用什么客户端,每一条链接、照片、视频,然后从最早发布的动态看起都会有很大收获,一般得到一个账号的密码就相当于得到了其他账号,同一个人的不同账号用户名几乎是相近或相同的。

优秀的个人博客,低调大师

排序算法入门之「插入排序」

插入排序 借用《算法导论》里的例子,就是我们打牌的时候,每新拿一张牌都会把它按顺序插入,这,其实就是插入排序。 齐姐声明:虽然我们用打牌的例子,但是可不能学胡适先生啊。 对于数组来说怎么做呢? 有一个重要的思想,叫做挡板法,就是用挡板把数组分成两个区间: 挡板左边:已排序 挡板右边:未排序 那么排序分三步走: 最初挡板是在数组的最左边,index = 0 的位置,也就是保证了已排序区间里一个数都没有,或者也可以包含一个数啦; 核心思想就是: 依次遍历未排序区间里的元素,在已排序区间里找到正确的位置插入; 重复这个过程,直到未排序区间为空。 举个例子:{5, 2, 1, 0} 第一步,挡板最初在这里: 第二步, 把 2 插入已排序区间的正确位置,变成: 重复这个步骤,把 1 排好: 最后把 0 排好: 那代码也很简单: public void insertionSort(int[] input) { if (input.length <= 1) { return; } for(int i = 1; i < input.length; i++) { int tmp = input[i]; int j = i - 1; while(j >= 0 && input[j] > tmp) { input[j+1] = input[j]; j --; } input[j+1] = tmp; } } 我们来分析一下这个算法的时空复杂度。 时间复杂度 关于时间复杂度有两个要点: 是描述随着自变量的增长,所需时间的增长率; 是渐近线复杂度,就是说 不看系数 只看最高阶项 那么我们关心的 worst case 的情况就是: 如果数组是近乎倒序的,每次插入都要在数组的第一个位置插入,那么已排序区间内的所有的元素都要往后移动一位,这一步平均是 O(n),那么重复 n 次就是 O(n^2). 空间复杂度 重点是一个峰值的概念,并不是累计使用的空间。 这里是 O(1) 没什么好说的。 引入一个概念:sorted in place,也就是原地排序。 原地排序就是指空间复杂度为 O(1) 的算法,因为没有占用额外的空间,就是原地打转嘛。 其实 in-place 的思想并不是只在排序算法里有,只不过排序算法是一个最广为人知的例子罢了。本质上就是一个节省使用空间的思想。 但是对于排序算法,只分析它的时空复杂度是不够的,还有另外一个重要指标: 稳定性 这个是排序算法的一个重要指标,意思是元素之间的相对顺序是否保持了不变。 比如说:{5, 2, 2, 1, 0} 这个数组排序完成后这里面的两个 2 的相对顺序没有变,那么这个排序就是一个稳定排序。 那有同学可能就想,顺序变了又有什么关系呢? 其实,在实际工作中我们排序的对象不会只是一个数字,而是一个个的对象 (object),那么先按照对象的一个性质来排序,再按照另一个性质来排序,那就不希望原来的那个顺序被改变了。好像有点抽象,我们举个例子。 比如在股票交易系统里,有买卖双方的报价,那是如何匹配的呢? 先按照价格排序; 在相等的价格中,按照出价的时间顺序来排序。 那么一搬来说系统会维持一个按时间排序的价格序列,那么此时只需要用一个具有稳定性的排序算法,再按照价格大小来排序就好了。因为稳定性的排序算法可以保持大小相同的两个对象仍维持着原来的时间顺序。 那么插入排序是否是稳定性的排序呢?答案是肯定的。因为在我们插入新元素的时候是从后往前检查,并不是像打牌的时候随便插一个位置不能保证相对顺序。 大家可以看下下面的动画 就非常清楚了~ 优化 插入排序其实是有很大的优化空间的,你可以搜一下“希尔排序”。 在刚开始学习的时候,深度固然重要,但因为广度不够,如果学的太深可能会很痛苦,一个知识点就无穷无尽的延展,这并不是一个高效的学习方式。 所以如果时间有限,就要做好深度和广度的平衡: 在常用常考的知识点上多花时间精力,追求深度; 在一些拓展性的知识点上点到为止,先知道有这么回事就行。 保持 open minded 的心态,后期就会有质的提高。 如果你喜欢这篇文章,记得给我点赞留言哦~你们的支持和认可,就是我创作的最大动力,我们下篇文章见! 我是小齐,纽约程序媛,终生学习者,每天晚上 9 点,云自习室里不见不散! 更多干货文章见我的 Github: https://github.com/xiaoqi6666/NYCSDE

优秀的个人博客,低调大师

spring框架入门之事务控制

1. 关于事务控制 事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 1.1 spring中事务控制API PlatformTransactionManager 接口提供事务操作的方法,包含三个具体的操作 public interface PlatformTransactionManager extends TransactionManager { // 获取事务状态信息 TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; // 提交事务 void commit(TransactionStatus var1) throws TransactionException; // 回滚事务 void rollback(TransactionStatus var1) throws TransactionException; } 开发中常用的实现类: org.springframework.jdbc.datasource.DataSourceTransactionManager :使用Spring JDBC或iBatis进行持久化数据时使用 spring中事务控制接口的结构 1.1.2 TransactionDefinition 事务的定义信息对象,包含如下方法: 获取事务对象名称:String getName() 获取事务隔离级别:int getIsolationLevel() 获取事务传播行为:int getPropagationBehavior() 获取事务超时时间:int getTimeout() 获取事务是否只读:boolean isReadOnly() 1.1.3 TransactionStatus 描述了某个时间点上事务对象的状态信息,包含6个具体的操作: 刷新事务:void flush() 获取是否存在储存点:boolean hasSavepoint() 获取事务是否完成:boolean isCompleted() 获取事务是否为新的事物:boolean isNewTransaction() 获取事务是否回滚:boolean isRollbackOnly() 设置事务回滚:void set RollbackOnly() 1.2 事务的隔离级别 事务的隔离界别反映事务提交并发访问时的处理态度 1.2.1 事务隔离的级别 ① ISOLATION_DEFAULT 默认级别,由 DBA 默认的设置来决定隔离级别,归属下列某一种 ② ISOLATION_READ_UNCOMMITTED 就是一个事务可以读取另一个未提交事务的数据。会出现脏读、不可重复读、幻读(隔离级别最低,但并发性高) ③ ISOLATION_READ_COMMITTED 就是一个事务要等另一个事务提交后才能读取数据,解决脏读问题。会出现不可重复读、幻读问题(锁定正在读取的行,适用于大多数系统,Oracle默认级别) ④ ISOLATION_REPEATABLE_READ 就是在开始读取数据(事务开启)时,不再允许修改操作,解决不可重复读问题。会出现幻读问题(锁定所读的所有行,MYSQL默认级别) ⑤ ISOLATION_SERALZABLE 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。(锁整表) 事务隔离级别由上到下依次提升,隔离级别越高,越能保证数据的完整性和一致性。但对数据库性能的消耗依次增加,并发执行效率依次下降。 大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle 少数数据库默认隔离级别为:Repeatable Read 比如:MySQL InnoDB 1.2.2 数据库读取时会出现的三种问题 ① Dirty reads:读脏数据。 也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。 ② non-repeatable reads:数据不可重复读。 比如事务A中两处读取数据price的值。在第一读的时候,price是100,然后事务B就把price的值改成 200;事务A再读一次,结果就发现,price竟然就变成200了,造成事务A数据混乱。 ③ phantom reads:幻象读数据。 这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是 non-repeatable reads 的不一致是因为他所要取的数据值被改变了(比如price)而 phantom reads 所要读的数据的不一致却是他的条件数据集发生变化了。 比如:执行 Select account.id where account.name="Bruce*",第一次读去了6个符合条件的id;第二次读取的时候,由于事务B把一个帐号的名字由"dd"改成"Bruce1",结果取出来了7个数据。 不可重复读的重点是修改:同样的条件,两次读发现值不一样; 幻读的重点在于新增或者删除:同样的条件,两次读发现得到的记录数不一样 1.2.3 数据隔离级别和出现的问题之间的关联 Dirty reads non-repeatable reads phantom reads READ_UNCOMMITTED Y Y Y READ_COMMITTED N Y Y REPEATABLE_READ N N Y SERALZABLE N N N 1.3 事务的传播行为 REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选 择(默认值) SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务) MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。 REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 NEVER:以非事务方式运行,如果当前存在事务,抛出异常。 NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。 1.4 超时时间 指事务提交后最长可以等待的时间,超出时间则会自动失败。默认值是-1,没有时间限制。如果有,则以秒为单位进行设置。 1.5 是否为只读事务 读写型事务:增加、删除、修改时开启事务 只读型事务:执行查询时,也会开启事务 2. 基于xml的事务控制配置 2.1 配置步骤 配置事务管理器 配置事务的通知 此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的使用 <tx:advice> 标签配置事务通知 id:给事务通知起一个唯一标识 transaction-manager:给事务通知提供一个事务管理器引用 配置AOP中的通用切入点表达式 建立事务通知和切入点表达式的对应关系 配置事务的属性是在事务的通知 <tx:advice> 标签的内部 2.2 配置事务的属性 isolation:用于指定事务的隔离级别。默认值是 DEFAULT,表示使用数据库的默认隔离级别。 propagation:用于指定事务的传播行为。默认值是 REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择 SUPPORTS。 read-only:用于指定事务是否只读。只有查询方法才能设置为 true。默认值是false,表示读写。 timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。 rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。 no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。 代码示例: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/base_crud"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> <!-- 1.配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2.配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method> <tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method> </tx:attributes> </tx:advice> <!-- 3.配置AOP --> <aop:config> <aop:pointcut id="pointcut" expression="execution(* cn.bruce.service.impl.*.*(..))"/> <!-- 4.建立事务通知和切入点表达式的对应关系 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor> </aop:config> <!-- 配置dao --> <bean id="accountDao" class="cn.bruce.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置service --> <bean id="accountService" class="cn.bruce.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> </beans> 3. 基于注解的事务控制配置 3.1 配置步骤 ① 书写配置类 ② 将对象注入到IoC容器中管理 ③ 给业务添加事务注释,并指明事务属性 代码示例: ① 配置spring @Configuration// 声明为配置类 @ComponentScan("cn.bruce")// 声明需要扫描的包 @Import({JdbcConfig.class, TransactionConfig.class})// 导入其他配置类 @PropertySource("jdbcConfig.properties")// 导入配置文件 @EnableAspectJAutoProxy(proxyTargetClass = true)// 开启注解支持 @EnableTransactionManagement// 开启事务控制 public class SpringConfiguration { } ② 配置jdbc public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 创建数据源对象 * @return */ @Bean("dataSource") public DataSource creatDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } /** * 创建JdbcTemplate * @param dataSource * @return */ @Bean(name = "jdbcTemplate") public JdbcTemplate creatJdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } } ③ 配置事务控制器 public class TransactionConfig { /** * 用于创建事务管理器对象 * @param dataSource * @return */ @Bean(name = "transactionManager") public PlatformTransactionManager creatTransactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ④ 通过 @Repository 和 @service 注解将Dao和Service层对象注入IoC容器 ⑤ 在业务层使用 @Transactional 注解进行事务配置 // 进行读写型事务配置 @Transactional(propagation = Propagation.REQUIRED, readOnly = false) @Override public void transfer(String sourceName, String targetName, Float money) { System.out.println("开始进行转账操作。。。"); Account source = accountDao.getAccountByName(sourceName); Account target = accountDao.getAccountByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.updateAccount(source); int i = 1/0; accountDao.updateAccount(target); System.out.println("转账完成。。。"); } ⑥ 书写测试类进行测试 3.2 涉及到的注解 @Transactional 此注解相当于xml配置中的 <tx:attributes>****</tx:attributes> 用于进行事务的配置,其属性含义和xml中是一致的 此注解可是使用在接口、类和方法上: 出现在接口上,表示此接口所有的实现类都有事务支持 出现在类上,表示类所有的方法都有事务支持 出现在方法上,表示此方法有事务支持 以上三个位置的优先级:方法 > 类 > 接口

优秀的个人博客,低调大师

Serverless Kubernetes 入门:对 Kubernetes 做减法

作者 | 贤维 阿里巴巴高级技术专家 导读:Serverless Kubernetes 是阿里云容器服务团队对未来 Kubernetes 演进方向的一种探索,通过对 Kubernetes 做减法,降低运维管理负担,简化集群管理,让 Kubernetes 从复杂到简单。 背景 Kubernetes 作为通用的容器编排系统,承载了广泛的应用和场景,包括 CI/CD,数据计算,在线应用,AI 等,然而由于其通用性和复杂性,管理一个 Kubernetes 集群对于很多用户而言还是充满挑战的,主要体现在: 学习成本高; 集群运维管理成本高,包括节点管理、容量规划,以及各种节点异常问题的定位; 计算成本在很多场景中没有达到最优,比如对于一个定时运行 Jobs 的集群,长期持有资源池对于用户来说是浪费的行为,资源利用率不高。 对 Kubernetes 集群

优秀的个人博客,低调大师

Tablestore入门手册-数据管理-GetRow

GetRow接口概述 GetRow接口用于读取一行数据,是Tablestore最基础的API之一。官方提供了Java、Go、Node.js、Python、PHP、C#、C++ SDK。本文以Java代码为例,对GetRow接口进行详细说明。 基本使用说明 参数说明 参数名称 是否必填 参数说明 PrimaryKey 是 主键,所有主键都需要填写 ColumnsToGet 否 需要读取的列的集合,若不设置则读取所有列 MaxVersions MaxVersions 与 TimeRange 至少设置一个 最多读取多少个版本 TimeRange MaxVersions 与 TimeRange 至少设置一个 要读取的版本范围 Filter 否 过滤器,在服务端对读取结果进行过滤 Java代码示例 public void getRow() { //构造主键,主键列必须全部指定 PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PK1, PrimaryKeyValue.fromLong(1L)); primaryKeyBuilder.addPrimaryKeyColumn(PK2, PrimaryKeyValue.fromString("string")); PrimaryKey primaryKey = primaryKeyBuilder.build(); SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); //设置读取的版本数为1,即读取最新版本 criteria.setMaxVersions(1); GetRowRequest getRowRequest = new GetRowRequest(criteria); GetRowResponse getRowResponse = syncClient.getRow(getRowRequest); Row row = getRowResponse.getRow(); System.out.println("读取行完毕, 结果为: "); System.out.println(row); } 多版本数据读取 表格存储支持多版本数据存储,具体参考开发指南。 1.MaxVersions 指定maxVersions,返回最新的几个版本。例如:某一行的某一列有20个历史版本,设置maxVersions为10,返回的是最新的10个版本。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); // 设置读取最新版本 criteria.setMaxVersions(10); 2.TimeRange 每一个版本都有一个对应的版本号,这个版本号默认是一个毫秒级的时间戳,用户也可以自己指定版本号,读取出来的时候可以按时间范围取值,返回范围内的版本列。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); TimeRange timeRange = new TimeRange(1418380771, 1418390771); criteria.setTimeRange(timeRange); 宽行读取 有很多数据列的行,我们称为宽行。因为数据列非常多,受网络带宽、延迟等因素的影响,我们无法一次性读出来,这里介绍几种宽行的读取方法。 1.使用ColumsToGet读取指定列 ColumsToGet参数,允许用户指定需要读取的列。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); // 设置读取某些列 criteria.addColumnsToGet(new String[] {"Col1","Col2","Col3"}); 2.使用startColumn,endColumn读取一定范围的属性列 通过startColumn和endColumn指定宽行中某个范围内的列,按照字典序对列进行排序比较。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); criteria.setStartColumn("col0"); criteria.setEndColumn("col3"); 3.使用ColumnPaginationFilter配合startColumn翻页读取 ColumnPaginationFilter有两个参数,limit和offset,使用ColumnPaginationFilter读取时,会跳过offset个属性列,然后读取limit个属性列。配合startColumn使用时,会从startColumn开始,跳过offset个属性列,读取limit个属性列。这种方式适合分页读取属性列的场景。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey); // 设置从Column0开始读 criteria.setStartColumn("Column0"); // 使用ColumnPaginationFilter设置一次要读取的列数, limit=10, offset=0 criteria.setFilter(new ColumnPaginationFilter(10, 0)); 使用过滤器 表格存储过滤器的过滤条件支持算术运算(=、!=、>、>=、<、<=)和逻辑运算(NOT、AND、OR),支持最多 10 个条件的组合。过滤器的具体使用说明请参考:过滤器使用说明。 1.单条件过滤器:SingleColumnValueFilter 表格存储中的数据是稀疏矩阵形式的,每一行的列可能都不一样,对于不存在某一列的行,可以使用PassIfMissing参数来设置期望的过滤形式。passIfMissing为True代表如果这一列不存在也返回,为false代表这一列不存在就不返回。 // 设置过滤器, 当 Col0 的值为 0 时返回该行。 SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter("Col0", SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromLong(0)); // 如果不存在 Col0 这一列, 也不返回。 singleColumnValueFilter.setPassIfMissing(false); 2.多条件过滤:CompositeColumnValueFilter 多条件过滤器,可以通过组合多个算术运算符号达到多条件组合过滤的效果。 // composite1 条件为 (Col0 == 0) AND (Col1 > 100) CompositeColumnValueFilter composite1 = new CompositeColumnValueFilter(CompositeColumnValueFilter.LogicOperator.AND); SingleColumnValueFilter single1 = new SingleColumnValueFilter("Col0", SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromLong(0)); SingleColumnValueFilter single2 = new SingleColumnValueFilter("Col1", SingleColumnValueFilter.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100)); composite1.addFilter(single1); composite1.addFilter(single2); // composite2 条件为 ( (Col0 == 0) AND (Col1 > 100) ) OR (Col2 <= 10) CompositeColumnValueFilter composite2 = new CompositeColumnValueFilter(CompositeColumnValueFilter.LogicOperator.OR); SingleColumnValueFilter single3 = new SingleColumnValueFilter("Col2", SingleColumnValueFilter.CompareOperator.LESS_EQUAL, ColumnValue.fromLong(10)); composite2.addFilter(composite1); composite2.addFilter(single3);

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册