Springboot JPA 的简单使用和字段排序问题

简单使用

在Springboot项目中引入 jpa

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

定义公共字段

import javax.persistence.*;
import java.util.Date;

@MappedSuperclass //告诉 jpa 这是公用字段
public class CommonModel {
	@Id
	@Column(columnDefinition = "bigint(64) COMMENT 'ID'")
	private Long id;

	@Column(columnDefinition = "varchar(32) COMMENT '创建者'")
	private String createBy;

	@Column(columnDefinition = "datetime COMMENT '创建时间'")
	private Date createTime;

	@Column(columnDefinition = "datetime COMMENT '更新者'")
	private String updateBy;

	@Column(columnDefinition = "datetime COMMENT '更新时间'")
	private Date updateTime;

	@Column(columnDefinition = "datetime COMMENT '备注'")
	private String remark;
}

定义表字段

import lombok.Data;
import javax.persistence.*;

@Entity
@Table(name = "data_catalog")
@Data
@org.hibernate.annotations.Table(appliesTo = "data_catalog",comment="数据目录") //告诉数据库这是表注释
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class DataCatalog extends CommonModel {
	@Column(columnDefinition = "varchar(32) COMMENT '目录名称'")
	private String name; // 目录名称
	@Column(columnDefinition = "bigint(64) DEFAULT 0 COMMENT '上级目录'")
	private Long parentId; // 上级目录
	@Column(columnDefinition = "varchar(32) COMMENT '目录描述'")
	private String description; // 目录描述
	@Column(columnDefinition = "int COMMENT '排序'")
	private Integer sort; // 排序
}

通过以上步骤就可以简单实现自动建表了!
当然配置文件上记得配置 spring.jpa.hibernate.ddl-auto = update
下面说一下排序问题

排序

需要重写jpa的排序逻辑
这里我们直接在java目录下新建两个类
原理就是修改最终的字段属性返回值实现重新排序
在这里插入图片描述
要记得这个报名和下面的类名完全一致!
这个是为了覆盖jap的源码,利用了jvm的加载类文件的原理实现的,不需要动jar包里面的类!

InheritanceState 代码

这里是修改这部分的代码,对 properties 的顺序进行了 改变
在这里插入图片描述

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.hibernate.cfg;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.persistence.Access;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.mapping.PersistentClass;

public class InheritanceState {
    private XClass clazz;
    private boolean hasSiblings = false;
    private boolean hasParents = false;
    private InheritanceType type;
    private boolean isEmbeddableSuperclass = false;
    private Map<XClass, InheritanceState> inheritanceStatePerClass;
    private List<XClass> classesToProcessForMappedSuperclass = new ArrayList();
    private MetadataBuildingContext buildingContext;
    private AccessType accessType;
    private InheritanceState.ElementsToProcess elementsToProcess;
    private Boolean hasIdClassOrEmbeddedId;

    public InheritanceState(XClass clazz, Map<XClass, InheritanceState> inheritanceStatePerClass, MetadataBuildingContext buildingContext) {
        this.setClazz(clazz);
        this.buildingContext = buildingContext;
        this.inheritanceStatePerClass = inheritanceStatePerClass;
        this.extractInheritanceType();
    }

    private void extractInheritanceType() {
        XAnnotatedElement element = this.getClazz();
        Inheritance inhAnn = (Inheritance)element.getAnnotation(Inheritance.class);
        MappedSuperclass mappedSuperClass = (MappedSuperclass)element.getAnnotation(MappedSuperclass.class);
        if (mappedSuperClass != null) {
            this.setEmbeddableSuperclass(true);
            this.setType(inhAnn == null ? null : inhAnn.strategy());
        } else {
            this.setType(inhAnn == null ? InheritanceType.SINGLE_TABLE : inhAnn.strategy());
        }

    }

    boolean hasTable() {
        return !this.hasParents() || !InheritanceType.SINGLE_TABLE.equals(this.getType());
    }

    boolean hasDenormalizedTable() {
        return this.hasParents() && InheritanceType.TABLE_PER_CLASS.equals(this.getType());
    }

    public static InheritanceState getInheritanceStateOfSuperEntity(XClass clazz, Map<XClass, InheritanceState> states) {
        XClass superclass = clazz;

        do {
            superclass = superclass.getSuperclass();
            InheritanceState currentState = (InheritanceState)states.get(superclass);
            if (currentState != null && !currentState.isEmbeddableSuperclass()) {
                return currentState;
            }
        } while(superclass != null && !Object.class.getName().equals(superclass.getName()));

        return null;
    }

    public static InheritanceState getSuperclassInheritanceState(XClass clazz, Map<XClass, InheritanceState> states) {
        XClass superclass = clazz;

        do {
            superclass = superclass.getSuperclass();
            InheritanceState currentState = (InheritanceState)states.get(superclass);
            if (currentState != null) {
                return currentState;
            }
        } while(superclass != null && !Object.class.getName().equals(superclass.getName()));

        return null;
    }

    public XClass getClazz() {
        return this.clazz;
    }

    public void setClazz(XClass clazz) {
        this.clazz = clazz;
    }

    public boolean hasSiblings() {
        return this.hasSiblings;
    }

    public void setHasSiblings(boolean hasSiblings) {
        this.hasSiblings = hasSiblings;
    }

    public boolean hasParents() {
        return this.hasParents;
    }

    public void setHasParents(boolean hasParents) {
        this.hasParents = hasParents;
    }

    public InheritanceType getType() {
        return this.type;
    }

    public void setType(InheritanceType type) {
        this.type = type;
    }

    public boolean isEmbeddableSuperclass() {
        return this.isEmbeddableSuperclass;
    }

    public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
        this.isEmbeddableSuperclass = embeddableSuperclass;
    }

    void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
        this.getElementsToProcess();
        this.addMappedSuperClassInMetadata(persistenceClass);
        entityBinder.setPropertyAccessType(this.accessType);
    }

    public XClass getClassWithIdClass(boolean evenIfSubclass) {
        if (!evenIfSubclass && this.hasParents()) {
            return null;
        } else if (this.clazz.isAnnotationPresent(IdClass.class)) {
            return this.clazz;
        } else {
            InheritanceState state = getSuperclassInheritanceState(this.clazz, this.inheritanceStatePerClass);
            return state != null ? state.getClassWithIdClass(true) : null;
        }
    }

    public Boolean hasIdClassOrEmbeddedId() {
        if (this.hasIdClassOrEmbeddedId == null) {
            this.hasIdClassOrEmbeddedId = false;
            if (this.getClassWithIdClass(true) != null) {
                this.hasIdClassOrEmbeddedId = true;
            } else {
                InheritanceState.ElementsToProcess process = this.getElementsToProcess();
                Iterator var2 = process.getElements().iterator();

                while(var2.hasNext()) {
                    PropertyData property = (PropertyData)var2.next();
                    if (property.getProperty().isAnnotationPresent(EmbeddedId.class)) {
                        this.hasIdClassOrEmbeddedId = true;
                        break;
                    }
                }
            }
        }

        return this.hasIdClassOrEmbeddedId;
    }

    public InheritanceState.ElementsToProcess getElementsToProcess() {
        if (this.elementsToProcess == null) {
            InheritanceState inheritanceState = (InheritanceState)this.inheritanceStatePerClass.get(this.clazz);

            assert !inheritanceState.isEmbeddableSuperclass();

            this.getMappedSuperclassesTillNextEntityOrdered();
            this.accessType = this.determineDefaultAccessType();
            ArrayList<PropertyData> elements = new ArrayList();
            int idPropertyCount = 0;

            int currentIdPropertyCount;
            for(Iterator var4 = this.classesToProcessForMappedSuperclass.iterator(); var4.hasNext(); idPropertyCount += currentIdPropertyCount) {
                XClass classToProcessForMappedSuperclass = (XClass)var4.next();
                PropertyContainer propertyContainer = new PropertyContainer(classToProcessForMappedSuperclass, this.clazz, this.accessType);
                currentIdPropertyCount = AnnotationBinder.addElementsOfClass(elements, propertyContainer, this.buildingContext);
            }

            if (idPropertyCount == 0 && !inheritanceState.hasParents()) {
                throw new AnnotationException("No identifier specified for entity: " + this.clazz.getName());
            }

            elements.trimToSize();
            this.elementsToProcess = new InheritanceState.ElementsToProcess(elements, idPropertyCount);
        }

        return this.elementsToProcess;
    }

    private AccessType determineDefaultAccessType() {
        XClass xclass;
        for(xclass = this.clazz; xclass != null; xclass = xclass.getSuperclass()) {
            if ((xclass.getSuperclass() == null || Object.class.getName().equals(xclass.getSuperclass().getName())) && (xclass.isAnnotationPresent(Entity.class) || xclass.isAnnotationPresent(MappedSuperclass.class)) && xclass.isAnnotationPresent(Access.class)) {
                return AccessType.getAccessStrategy(((Access)xclass.getAnnotation(Access.class)).value());
            }
        }

        label62:
        for(xclass = this.clazz; xclass != null && !Object.class.getName().equals(xclass.getName()); xclass = xclass.getSuperclass()) {
            if (xclass.isAnnotationPresent(Entity.class) || xclass.isAnnotationPresent(MappedSuperclass.class)) {
                Iterator var2 = xclass.getDeclaredProperties(AccessType.PROPERTY.getType()).iterator();

                XProperty prop;
                boolean isEmbeddedId;
                do {
                    if (!var2.hasNext()) {
                        var2 = xclass.getDeclaredProperties(AccessType.FIELD.getType()).iterator();

                        do {
                            if (!var2.hasNext()) {
                                continue label62;
                            }

                            prop = (XProperty)var2.next();
                            isEmbeddedId = prop.isAnnotationPresent(EmbeddedId.class);
                        } while(!prop.isAnnotationPresent(Id.class) && !isEmbeddedId);

                        return AccessType.FIELD;
                    }

                    prop = (XProperty)var2.next();
                    isEmbeddedId = prop.isAnnotationPresent(EmbeddedId.class);
                } while(!prop.isAnnotationPresent(Id.class) && !isEmbeddedId);

                return AccessType.PROPERTY;
            }
        }

        throw new AnnotationException("No identifier specified for entity: " + this.clazz);
    }

    private void getMappedSuperclassesTillNextEntityOrdered() {
        XClass currentClassInHierarchy = this.clazz;

        InheritanceState superclassState;
        do {
            this.classesToProcessForMappedSuperclass.add(0, currentClassInHierarchy);
            XClass superClass = currentClassInHierarchy;

            do {
                superClass = superClass.getSuperclass();
                superclassState = (InheritanceState)this.inheritanceStatePerClass.get(superClass);
            } while(superClass != null && !this.buildingContext.getBuildingOptions().getReflectionManager().equals(superClass, Object.class) && superclassState == null);

            currentClassInHierarchy = superClass;
        } while(superclassState != null && superclassState.isEmbeddableSuperclass());

    }

    private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
        org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
        InheritanceState superEntityState = getInheritanceStateOfSuperEntity(this.clazz, this.inheritanceStatePerClass);
        PersistentClass superEntity = superEntityState != null ? this.buildingContext.getMetadataCollector().getEntityBinding(superEntityState.getClazz().getName()) : null;
        int lastMappedSuperclass = this.classesToProcessForMappedSuperclass.size() - 1;

        for(int index = 0; index < lastMappedSuperclass; ++index) {
            org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
            Class<?> type = this.buildingContext.getBuildingOptions().getReflectionManager().toClass((XClass)this.classesToProcessForMappedSuperclass.get(index));
            mappedSuperclass = this.buildingContext.getMetadataCollector().getMappedSuperclass(type);
            if (mappedSuperclass == null) {
                mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity);
                mappedSuperclass.setMappedClass(type);
                this.buildingContext.getMetadataCollector().addMappedSuperclass(type, mappedSuperclass);
            }
        }

        if (mappedSuperclass != null) {
            persistentClass.setSuperMappedSuperclass(mappedSuperclass);
        }

    }

    static final class ElementsToProcess {
        private final List<PropertyData> properties;
        private final int idPropertyCount;

        public List<PropertyData> getElements() {
            return this.properties;
        }

        public int getIdPropertyCount() {
            return this.idPropertyCount;
        }

        private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
            List<PropertyData> perList = new ArrayList<>();
            List<PropertyData> finalList = new ArrayList<>();
            properties.forEach(item->{
                XClass last = properties.get(properties.size()-1).getDeclaringClass();
                String name = item.getProperty().getName();
                if(item.getDeclaringClass()!=last && !name.equals("id") ){
                    finalList.add(item);
                }else{
                    perList.add(item);
                }
            });
            perList.addAll(finalList);
            this.properties = perList;
            this.idPropertyCount = idPropertyCount;
        }
    }
}

PropertyContainer 代码

这部分代码是改变了 属性存储对象的类型 把Map 改成 LinkedHashMap 实现有序!

package org.hibernate.cfg;

import org.hibernate.AnnotationException;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.MappingException;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
import javax.persistence.*;
import java.util.*;

/**
 * @Author: 凤凰[小哥哥]
 * @Date: 2019/9/20 16:12
 * @Email: 15290810931@163.com
 */
public class PropertyContainer {

    private static final CoreMessageLogger LOG = (CoreMessageLogger) Logger.getMessageLogger(CoreMessageLogger.class, PropertyContainer.class.getName());
    private final XClass xClass;
    private final XClass entityAtStake;
    private final AccessType classLevelAccessType;
    private final LinkedHashMap<String, XProperty> persistentAttributeMap;

    PropertyContainer(XClass clazz, XClass entityAtStake, AccessType defaultClassLevelAccessType) {
        this.xClass = clazz;
        this.entityAtStake = entityAtStake;
        if (defaultClassLevelAccessType == AccessType.DEFAULT) {
            defaultClassLevelAccessType = AccessType.PROPERTY;
        }
        AccessType localClassLevelAccessType = this.determineLocalClassDefinedAccessStrategy();
        assert localClassLevelAccessType != null;
        this.classLevelAccessType = localClassLevelAccessType != AccessType.DEFAULT ? localClassLevelAccessType : defaultClassLevelAccessType;
        assert this.classLevelAccessType == AccessType.FIELD || this.classLevelAccessType == AccessType.PROPERTY;
        this.persistentAttributeMap = new LinkedHashMap();
        List<XProperty> fields = this.xClass.getDeclaredProperties(AccessType.FIELD.getType());
        List<XProperty> getters = this.xClass.getDeclaredProperties(AccessType.PROPERTY.getType());
        this.preFilter(fields, getters);
        Map<String, XProperty> persistentAttributesFromGetters = new HashMap();
        this.collectPersistentAttributesUsingLocalAccessType(this.persistentAttributeMap, persistentAttributesFromGetters, fields, getters);
        this.collectPersistentAttributesUsingClassLevelAccessType(this.persistentAttributeMap, persistentAttributesFromGetters, fields, getters);
        int i=0;
    }

    private void preFilter(List<XProperty> fields, List<XProperty> getters) {
        Iterator propertyIterator = fields.iterator();
        XProperty property;
        while (propertyIterator.hasNext()) {
            property = (XProperty) propertyIterator.next();
            if (mustBeSkipped(property)) {
                propertyIterator.remove();
            }
        }

        propertyIterator = getters.iterator();
        while (propertyIterator.hasNext()) {
            property = (XProperty) propertyIterator.next();
            if (mustBeSkipped(property)) {
                propertyIterator.remove();
            }
        }

    }

    private void collectPersistentAttributesUsingLocalAccessType(LinkedHashMap<String, XProperty> persistentAttributeMap, Map<String, XProperty> persistentAttributesFromGetters, List<XProperty> fields, List<XProperty> getters) {
        Iterator propertyIterator = fields.iterator();
        XProperty xProperty;
        Access localAccessAnnotation;
        while (propertyIterator.hasNext()) {
            xProperty = (XProperty) propertyIterator.next();
            localAccessAnnotation = (Access) xProperty.getAnnotation(Access.class);
            if (localAccessAnnotation != null && localAccessAnnotation.value() == javax.persistence.AccessType.FIELD) {
                propertyIterator.remove();
                persistentAttributeMap.put(xProperty.getName(), xProperty);
            }
        }

        propertyIterator = getters.iterator();

        while (propertyIterator.hasNext()) {
            xProperty = (XProperty) propertyIterator.next();
            localAccessAnnotation = (Access) xProperty.getAnnotation(Access.class);
            if (localAccessAnnotation != null && localAccessAnnotation.value() == javax.persistence.AccessType.PROPERTY) {
                propertyIterator.remove();
                String name = xProperty.getName();
                XProperty previous = (XProperty) persistentAttributesFromGetters.get(name);
                if (previous != null) {
                    throw new MappingException(LOG.ambiguousPropertyMethods(this.xClass.getName(), HCANNHelper.annotatedElementSignature(previous), HCANNHelper.annotatedElementSignature(xProperty)), new Origin(SourceType.ANNOTATION, this.xClass.getName()));
                }
                persistentAttributeMap.put(name, xProperty);
                persistentAttributesFromGetters.put(name, xProperty);
            }
        }
    }

    private void collectPersistentAttributesUsingClassLevelAccessType(LinkedHashMap<String, XProperty> persistentAttributeMap, Map<String, XProperty> persistentAttributesFromGetters, List<XProperty> fields, List<XProperty> getters) {
        Iterator var5;
        XProperty getter;
        if (this.classLevelAccessType == AccessType.FIELD) {
            var5 = fields.iterator();
            while (var5.hasNext()) {
                getter = (XProperty) var5.next();
                if (!persistentAttributeMap.containsKey(getter.getName())) {
                    persistentAttributeMap.put(getter.getName(), getter);
                }
            }
        } else {
            var5 = getters.iterator();
            while (var5.hasNext()) {
                getter = (XProperty) var5.next();
                String name = getter.getName();
                XProperty previous = (XProperty) persistentAttributesFromGetters.get(name);
                if (previous != null) {
                    throw new MappingException(LOG.ambiguousPropertyMethods(this.xClass.getName(), HCANNHelper.annotatedElementSignature(previous), HCANNHelper.annotatedElementSignature(getter)), new Origin(SourceType.ANNOTATION, this.xClass.getName()));
                }
                if (!persistentAttributeMap.containsKey(name)) {
                    persistentAttributeMap.put(getter.getName(), getter);
                    persistentAttributesFromGetters.put(name, getter);
                }
            }
        }
    }

    public XClass getEntityAtStake() {
        return this.entityAtStake;
    }

    public XClass getDeclaringClass() {
        return this.xClass;
    }

    public AccessType getClassLevelAccessType() {
        return this.classLevelAccessType;
    }

    public Collection<XProperty> getProperties() {
        this.assertTypesAreResolvable();
        return Collections.unmodifiableCollection(this.persistentAttributeMap.values());
    }

    private void assertTypesAreResolvable() {
        Iterator var1 = this.persistentAttributeMap.values().iterator();
        XProperty xProperty;
        do {
            if (!var1.hasNext()) {
                return;
            }
            xProperty = (XProperty) var1.next();
        } while (xProperty.isTypeResolved() || discoverTypeWithoutReflection(xProperty));

        String msg = "Property " + StringHelper.qualify(this.xClass.getName(), xProperty.getName()) + " has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type";
        throw new AnnotationException(msg);
    }

    private AccessType determineLocalClassDefinedAccessStrategy() {
        AccessType hibernateDefinedAccessType = AccessType.DEFAULT;
        AccessType jpaDefinedAccessType = AccessType.DEFAULT;
        org.hibernate.annotations.AccessType accessType = (org.hibernate.annotations.AccessType) this.xClass.getAnnotation(org.hibernate.annotations.AccessType.class);
        if (accessType != null) {
            hibernateDefinedAccessType = AccessType.getAccessStrategy(accessType.value());
        }
        Access access = (Access) this.xClass.getAnnotation(Access.class);
        if (access != null) {
            jpaDefinedAccessType = AccessType.getAccessStrategy(access.value());
        }
        if (hibernateDefinedAccessType != AccessType.DEFAULT && jpaDefinedAccessType != AccessType.DEFAULT && hibernateDefinedAccessType != jpaDefinedAccessType) {
            throw new org.hibernate.MappingException("@AccessType and @Access specified with contradicting values. Use of @Access only is recommended. ");
        } else {
            AccessType classDefinedAccessType;
            if (hibernateDefinedAccessType != AccessType.DEFAULT) {
                classDefinedAccessType = hibernateDefinedAccessType;
            } else {
                classDefinedAccessType = jpaDefinedAccessType;
            }
            return classDefinedAccessType;
        }
    }

    private static boolean discoverTypeWithoutReflection(XProperty p) {
        if (p.isAnnotationPresent(OneToOne.class) && !((OneToOne) p.getAnnotation(OneToOne.class)).targetEntity().equals(Void.TYPE)) {
            return true;
        } else if (p.isAnnotationPresent(OneToMany.class) && !((OneToMany) p.getAnnotation(OneToMany.class)).targetEntity().equals(Void.TYPE)) {
            return true;
        } else if (p.isAnnotationPresent(ManyToOne.class) && !((ManyToOne) p.getAnnotation(ManyToOne.class)).targetEntity().equals(Void.TYPE)) {
            return true;
        } else if (p.isAnnotationPresent(ManyToMany.class) && !((ManyToMany) p.getAnnotation(ManyToMany.class)).targetEntity().equals(Void.TYPE)) {
            return true;
        } else if (p.isAnnotationPresent(Any.class)) {
            return true;
        } else if (p.isAnnotationPresent(ManyToAny.class)) {
            if (!p.isCollection() && !p.isArray()) {
                throw new AnnotationException("@ManyToAny used on a non collection non array property: " + p.getName());
            } else {
                return true;
            }
        } else if (p.isAnnotationPresent(Type.class)) {
            return true;
        } else {
            return p.isAnnotationPresent(Target.class);
        }
    }
    private static boolean mustBeSkipped(XProperty property) {
        return property.isAnnotationPresent(Transient.class) || "net.sf.cglib.transform.impl.InterceptFieldCallback".equals(property.getType().getName()) || "org.hibernate.bytecode.internal.javassist.FieldHandler".equals(property.getType().getName());
    }
}

好了,最后篇幅有限,如果有问题的朋友可以私信我或者评论区讨论哈

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值