大数据HBase JavaAPI工具类


1 环境准备

新建项目后在 pom.xml 中添加依赖:

<dependency>
 <groupId>org.apache.hbase</groupId>
 <artifactId>hbase-server</artifactId>
 <version>1.3.1</version>
</dependency>
<dependency>
 <groupId>org.apache.hbase</groupId>
 <artifactId>hbase-client</artifactId>
 <version>1.3.1</version>
</dependency>

2 HBaseAPI

2.1 获取 Configuration 对象

public static Configuration conf;
static{
//使用 HBaseConfiguration 的单例方法实例化
    conf = HBaseConfiguration.create();
    conf.set("hbase.zookeeper.quorum", "192.166.9.102");
    conf.set("hbase.zookeeper.property.clientPort", "2181");
    }

//或者注入到spring
@Configuration
@ConfigurationProperties(prefix = "hbase")
public class HbaseConfiguration {

    private Map<String, String> config = new HashMap<>();

    public Map<String, String> getConfig() {
        return config;
    }

    public void setConfig(Map<String, String> config) {
        this.config = config;
    }

    public org.apache.hadoop.conf.Configuration configuration() {
        org.apache.hadoop.conf.Configuration configuration = HBaseConfiguration.create();
        for(Map.Entry<String, String> map : config.entrySet()){
            configuration.set(map.getKey(), map.getValue());
        }
        return configuration;
    }

    @Bean
    public Admin admin() {
        Admin admin = null;
        try {
            Connection connection = ConnectionFactory.createConnection(configuration());
            admin = connection.getAdmin();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return admin;
    }

}

2.2 判断表是否存在

    /**
     * 判断表是否存在 新版本
     * @param tableName 表名
     * @return true/false
     */
    public boolean isExists(String tableName) {
        boolean tableExists = false;
        try {
            TableName table = TableName.valueOf(tableName);
            tableExists = hbaseAdmin.tableExists(table);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tableExists;
    }

HBaseAdmin对象都是DDL操作表,

2.3 创建表

public static void createTable(String tableName, String... columnFamily) throws MasterNotRunningException, ZooKeeperConnectionException,IOException{
    HBaseAdmin admin = new HBaseAdmin(conf);
//判断表是否存在
    if(isTableExist(tableName)){
    System.out.println("表" + tableName + "已存在");
//System.exit(0);
    }else{
//创建表属性对象,表名需要转字节
    HTableDescriptor descriptor = new
    HTableDescriptor(TableName.valueOf(tableName));
//创建多个列族
    for(String cf : columnFamily){
//添加列族信息
    descriptor.addFamily(new HColumnDescriptor(cf));
    }
//根据对表的配置,创建表
    admin.createTable(descriptor);
    System.out.println("表" + tableName + "创建成功!");
    }
   }

//封装好的方法,应该在创建的时候先创建最终的方法然后再逐步完善参数,但是熟悉的话可以正着写不过去了公司这都是封装好的知道有这个东西就好
    /*应该先做一个columnFamily的非空判断*/
    public boolean createTable(String tableName, List<String> columnFamily, List<String> keys) {
        if (!isExists(tableName)) {
            try {
                TableName table = TableName.valueOf(tableName);
                HTableDescriptor desc = new HTableDescriptor(table);
                for (String cf : columnFamily) {
                    //创建列族描述器
                    desc.addFamily(new HColumnDescriptor(cf));
                }
                if (keys == null) {
                    hbaseAdmin.createTable(desc);
                } else {
                    byte[][] splitKeys = getSplitKeys(keys);
                    hbaseAdmin.createTable(desc, splitKeys);
                }
                return true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(tableName + "is exists!!!");
            return false;
        }
        return false;
    }

2.4 删除表

public static void dropTable(String tableName) throws
    MasterNotRunningException,
    ZooKeeperConnectionException, IOException{
    HBaseAdmin admin = new HBaseAdmin(conf);
    if(isTableExist(tableName)){
    admin.disableTable(tableName);
    admin.deleteTable(tableName);
    System.out.println("表" + tableName + "删除成功!");
    }else{
    System.out.println("表" + tableName + "不存在!");
    } 
}

命名空间管理(一般不指定命名空间是再default) 命名空间可以被创建、移除、修改。
表和命名空间的隶属关系在在创建表时决定,通过以下格式指定: 当为一张表指定命名空间之后,对表的操作都要加命名空间,否则会找不到表。 相关shell操作如下所示: 创建一个命名空间 根据命名空间创建表 此时命名空间namespace应该存在,否则报错。 删除命名空间
在删除一个命名空间时,该命名空间不能包含任何的表,否则会报错。
修改命名空间 METHOD => ‘set’ ‘PROPERTY_NAME’ => ‘PROPERTY_VALUE’ 显示所有命名空间

在这里插入图片描述

2.5 向表中插入数据

public static void addRowData(String tableName, String rowKey,
    String columnFamily, String
    column, String value) throws IOException{
//创建 HTable 对象
    HTable hTable = new HTable(conf, tableName);
//向表中插入数据
    Put put = new Put(Bytes.toBytes(rowKey));
//向 Put 对象中组装数据
    put.add(Bytes.toBytes(columnFamily), Bytes.toBytes(column),
    Bytes.toBytes(value));
    hTable.put(put);
    hTable.close();
    System.out.println("插入数据成功");
    }

单条和封装键值对查询数据

    /**
     * 插入数据(单条)
     *
     * @param tableName    表名
     * @param rowKey       rowKey
     * @param columnFamily 列族
     * @param column       列
     * @param value        值
     * @return true/false
     */
    public boolean putData(String tableName, String rowKey, String columnFamily, String column,
                           String value) {
        //键值对
        return putData(tableName, rowKey, columnFamily, Collections.singletonMap(column, value));
    }

    /**
     * 插入数据(批量需要遍历插入数据)
     *
     * @param tableName    表名
     * @param rowKey       rowKey
     * @param columnFamily 列族
     * @param columns      列值
     * @return true/false
     */
    public boolean putData(String tableName, String rowKey, String columnFamily,
                           Map<String, String> columns) {
        try {
            Table table = hbaseAdmin.getConnection().getTable(TableName.valueOf(tableName));
            Put put = new Put(Bytes.toBytes(rowKey));
            for (Map.Entry<String, String> entry : columns.entrySet()) {
                put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(entry.getKey()),
                    Bytes.toBytes(entry.getValue()));
            }
            table.put(put);
            table.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

2.6 删除多行数据

public static void deleteMultiRow(String tableName, String... rows)
    throws IOException{
    HTable hTable = new HTable(conf, tableName);
    List<Delete> deleteList = new ArrayList<Delete>();
    for(String row : rows){
    Delete delete = new Delete(Bytes.toBytes(row));
    deleteList.add(delete);
    }
    hTable.delete(deleteList);
    hTable.close();
    }

2.7 获取所有数据

public static void getAllRows(String tableName) throws IOException{
    HTable hTable = new HTable(conf, tableName);
//得到用于扫描 region 的对象
    Scan scan = new Scan();
//使用 HTable 得到 resultcanner 实现类的对象
    ResultScanner resultScanner = hTable.getScanner(scan);
    for(Result result : resultScanner){
    Cell[] cells = result.rawCells();
    for(Cell cell : cells){
//得到 rowkey
    System.out.println(" 行 键 :" +
    Bytes.toString(CellUtil.cloneRow(cell)));
//得到列族
    System.out.println(" 列 族 " +
    Bytes.toString(CellUtil.cloneFamily(cell)));
    System.out.println(" 列 :" +
    Bytes.toString(CellUtil.cloneQualifier(cell)));
    System.out.println(" 值 :" +
    Bytes.toString(CellUtil.cloneValue(cell)));
    } 
  } 
}

2.8 获取某一行数据

public static void getRow(String tableName, String rowKey) throws
    IOException{
    HTable table = new HTable(conf, tableName);
    Get get = new Get(Bytes.toBytes(rowKey));
//get.setMaxVersions();显示所有版本
    //get.setTimeStamp();显示指定时间戳的版本
    Result result = table.get(get);
    for(Cell cell : result.rawCells()){
    System.out.println(" 行 键 :" +Bytes.toString(result.getRow()));
    System.out.println(" 列 族 " +
    Bytes.toString(CellUtil.cloneFamily(cell)));
    System.out.println(" 列 :" +
    Bytes.toString(CellUtil.cloneQualifier(cell)));
    System.out.println(" 值 :" +
    Bytes.toString(CellUtil.cloneValue(cell)));
    System.out.println("时间戳:" + cell.getTimestamp());
    } 
}

2.9 获取某一行指定“列族:列”的数据

public static void getRowQualifier(String tableName, String rowKey,
    String family, String
    qualifier) throws IOException{
    HTable table = new HTable(conf, tableName);
    Get get = new Get(Bytes.toBytes(rowKey));
    get.addColumn(Bytes.toBytes(family),
    Bytes.toBytes(qualifier));
    Result result = table.get(get);
    for(Cell cell : result.rawCells()){
    System.out.println(" 行 键 :" +
    Bytes.toString(result.getRow()));
    System.out.println(" 列 族 " +
    Bytes.toString(CellUtil.cloneFamily(cell)));
    System.out.println(" 列 :" +
    Bytes.toString(CellUtil.cloneQualifier(cell)));
    System.out.println(" 值 :" +
    Bytes.toString(CellUtil.cloneValue(cell)));
    } 
}

3 HBase过滤器过滤数据

在这里插入图片描述
测试数据生成:

package demo.filter;

import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;

public class DataInit {
	@Test
	public void testCreateTable() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//创建一个HBase客户端: HBaseAdmin
		HBaseAdmin admin = new HBaseAdmin(conf);
		
		//创建一个表的描述符: 表名
		HTableDescriptor hd = new HTableDescriptor(TableName.valueOf("emp"));
		
		//创建列族描述符
		HColumnDescriptor hcd1 = new HColumnDescriptor("empinfo");
		
		//加入列族
		hd.addFamily(hcd1);
		
		//创建表
		admin.createTable(hd);
		
		//关闭客户端
		admin.close();
	}

	@Test
	public void testPutData() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//第一条数据
		Put put1 = new Put(Bytes.toBytes("7369"));
		put1.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("SMITH"));
		Put put2 = new Put(Bytes.toBytes("7369"));
		put2.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("800"));

		//第二条数据
		Put put3 = new Put(Bytes.toBytes("7499"));
		put3.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("ALLEN"));
		Put put4 = new Put(Bytes.toBytes("7499"));
		put4.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1600"));		
		
		//第三条数据
		Put put5 = new Put(Bytes.toBytes("7521"));
		put5.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("WARD"));
		Put put6 = new Put(Bytes.toBytes("7521"));
		put6.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1250"));		
		
		//第四条数据
		Put put7 = new Put(Bytes.toBytes("7566"));
		put7.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("JONES"));
		Put put8 = new Put(Bytes.toBytes("7566"));
		put8.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("2975"));		

		//第五条数据
		Put put9 = new Put(Bytes.toBytes("7654"));
		put9.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("MARTIN"));
		Put put10 = new Put(Bytes.toBytes("7654"));
		put10.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1250"));

		//第六条数据
		Put put11 = new Put(Bytes.toBytes("7698"));
		put11.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("BLAKE"));
		Put put12 = new Put(Bytes.toBytes("7698"));
		put12.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("2850"));

		//第七条数据
		Put put13 = new Put(Bytes.toBytes("7782"));
		put13.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("CLARK"));
		Put put14 = new Put(Bytes.toBytes("7782"));
		put14.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("2450"));

		//第八条数据
		Put put15 = new Put(Bytes.toBytes("7788"));
		put15.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("SCOTT"));
		Put put16 = new Put(Bytes.toBytes("7788"));
		put16.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("3000"));		

		//第九条数据
		Put put17 = new Put(Bytes.toBytes("7839"));
		put17.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("KING"));
		Put put18 = new Put(Bytes.toBytes("7839"));
		put18.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("5000"));	

		//第十条数据
		Put put19 = new Put(Bytes.toBytes("7844"));
		put19.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("TURNER"));
		Put put20 = new Put(Bytes.toBytes("7844"));
		put20.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1500"));	

		//第十一条数据
		Put put21 = new Put(Bytes.toBytes("7876"));
		put21.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("ADAMS"));
		Put put22 = new Put(Bytes.toBytes("7876"));
		put22.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1100"));	

		//第十二条数据
		Put put23 = new Put(Bytes.toBytes("7900"));
		put23.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("JAMES"));
		Put put24 = new Put(Bytes.toBytes("7900"));
		put24.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("950"));

		//第十三条数据
		Put put25 = new Put(Bytes.toBytes("7902"));
		put25.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("FORD"));
		Put put26 = new Put(Bytes.toBytes("7902"));
		put26.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("3000"));

		//第十四条数据
		Put put27 = new Put(Bytes.toBytes("7934"));
		put27.add(Bytes.toBytes("empinfo"), Bytes.toBytes("ename"), Bytes.toBytes("MILLER"));
		Put put28 = new Put(Bytes.toBytes("7934"));
		put28.add(Bytes.toBytes("empinfo"), Bytes.toBytes("sal"), Bytes.toBytes("1300"));
	
		//构造List
		List<Put> list = new ArrayList<Put>();
		list.add(put1);
		list.add(put2);
		list.add(put3);
		list.add(put4);
		list.add(put5);
		list.add(put6);
		list.add(put7);
		list.add(put8);
		list.add(put9);
		list.add(put10);
		list.add(put11);
		list.add(put12);
		list.add(put13);
		list.add(put14);
		list.add(put15);
		list.add(put16);
		list.add(put17);
		list.add(put18);
		list.add(put19);
		list.add(put20);
		list.add(put21);
		list.add(put22);
		list.add(put23);
		list.add(put24);
		list.add(put25);
		list.add(put26);
		list.add(put27);
		list.add(put28);		
		
		//插入数据
		table.put(list);
		table.close();		
	}
}



过滤的demo:

package demo.filter;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.FilterList.Operator;
import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;

public class FilterDemo {

//1、列值过滤器:根据列的值进行查询 where sal=3000
	@Test
	public void testSingleColumnValueFilter() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//创建一个扫描器和列值过滤器
		Scan scan = new Scan();
		SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("empinfo"),     //列族
																	 Bytes.toBytes("sal"),     //列
				                                                     CompareOp.EQUAL,  //比较运算符
				                                                     Bytes.toBytes("3000"));
		scan.setFilter(filter);
		
		//查询数据
		ResultScanner rs = table .getScanner(scan);
		for(Result r:rs) {
			//取出数据
			String name = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("ename")));
			System.out.println(name);			
		}
		
		table.close();
	}
	
//2、列名前缀过滤器:查询指定的列,查询员工的姓名  select ename from emp;
	@Test
	public void testColumnPrefixFilter() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//创建一个扫描器和列名前缀过滤器
		Scan scan = new Scan();
		ColumnPrefixFilter filter = new ColumnPrefixFilter(Bytes.toBytes("ename"));
		scan.setFilter(filter);
		
		//查询数据
		ResultScanner rs = table .getScanner(scan);
		for(Result r:rs) {
			//取出数据
			String name = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("ename")));
			System.out.println(name);			
		}		
		
		table.close();
	}
	
	
//3、多个列名前缀过滤器:查询指定多个列,查询员工的姓名和薪水:select ename,sal from emp;
	@Test
	public void testMultipleColumnPrefixFilter() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//创建一个扫描器和多个列名前缀过滤器
		Scan scan = new Scan();
		byte[][] prefix = {Bytes.toBytes("ename"),Bytes.toBytes("sal")};
		MultipleColumnPrefixFilter filter = new MultipleColumnPrefixFilter(prefix);
		
		scan.setFilter(filter);
		
		//查询数据
		ResultScanner rs = table .getScanner(scan);
		for(Result r:rs) {
			//取出数据
			String name = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("ename")));
			String sal = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("sal")));
			System.out.println(name+"\t"+sal);			
		}				
		
		table.close();
	}
		
//4、Rowkey过滤器:通过行键进行查询
	@Test
	public void testRowFilter() throws Exception{
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//创建一个扫描器
		Scan scan = new Scan();
		//定义一个行键过滤器
		//查询行键是7839的员工信息
		RowFilter filter = new RowFilter(CompareOp.EQUAL,   //比较运算符 
				                         new RegexStringComparator("7839"));  //使用正则表达式表示值
		scan.setFilter(filter);
		
		//查询数据
		ResultScanner rs = table .getScanner(scan);
		for(Result r:rs) {
			//取出数据
			String name = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("ename")));
			String sal = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("sal")));
			System.out.println(name+"\t"+sal);			
		}	
		
		table.close();
	}
	
//5、查询中,组合多个过滤器
	@Test
	public void testFilters() throws Exception{
		/*
		 * 查询工资等于3000的员工姓名
		 * 1、列值过滤器,得到工资等于3000
		 * 2、列名前缀的过滤器,得到姓名
		 */
		//指定的配置信息: ZooKeeper
		Configuration conf = new Configuration();
		conf.set("hbase.zookeeper.quorum", "192.168.157.111");
		
		//客户端
		HTable table = new HTable(conf, "emp");
		
		//创建一个扫描器
		Scan scan = new Scan();
		
		//第一个过滤器  列值过滤器,得到工资等于3000
		SingleColumnValueFilter filter1 = new SingleColumnValueFilter(Bytes.toBytes("empinfo"),     //列族
																	 Bytes.toBytes("sal"),     //列
													                CompareOp.EQUAL,  //比较运算符
													                Bytes.toBytes("3000"));		
		//第二个过滤器:列名前缀过滤器,得到员工的姓名
		ColumnPrefixFilter filter2 = new ColumnPrefixFilter(Bytes.toBytes("ename"));
		
		//Operator.MUST_PASS_ALL:相当于是and
		//Operator.MUST_PASS_ONE:相当于是or
		FilterList list = new FilterList(Operator.MUST_PASS_ALL);
		list.addFilter(filter1);
		list.addFilter(filter2);
		
		scan.setFilter(list);
		
		//查询数据
		ResultScanner rs = table .getScanner(scan);
		for(Result r:rs) {
			//取出数据
			String name = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("ename")));
			String sal = Bytes.toString(r.getValue(Bytes.toBytes("empinfo"), Bytes.toBytes("sal")));
			System.out.println(name+"\t"+sal);			
		}			
		
		table.close();
	}

}
















  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵广陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值