compass学习笔记

学习compass最好和hibernate对照着学,因为compass 和 hibernate有很多相似之处。

lucene 相当于 JDBC。compass就相当于 hibernate。

Lucene 中的 文档(Document) 相当于 JDBC中数据库的一个表(table)。

Lucene 中的 Filed 相当于 表中的字段。

一个Document中有很多个Filed,一个表中也有多个字段。

 

使用hibernate的步骤和使用compass的步骤基本一致:

 

使用hibernate时步骤一般为:

1.建立实体类,也就是Entity,如Student,Teacher,这些实体类。

   

2.配置实体类到数据库的映射。配置方法一般有两种:

  (1)使用xml。如(Student.hbm.xml)

  (2)使用annotation 注解 @Entity ,@Id等

    Hiberante中要求每个实体都要有个标识(Id),compass中也这么要求。

 

3.使用hibernate的API操作实体

 

Configuration cfg = new Configuration().configure(); //加载hibernate.cfg.xml文件

SessionFactory sessionFactory =cfg.buildSessionFactory(); //全局唯一

Session session = sessionFactory.openSession();

session.beginTransaction(); //开启事务

session.save(xxx);//保存

session.delete(xxx);//删除

session.get(xxx.class,id)//获得对象

........//其他操作

session.getTransaction().commit(); //提交事务

session.close(); //关闭session

   

而使用compass 和 hibernate的步骤几乎是一样的:

 

1.建立实体类,如Student,Teacher等实体类。

 

2.配置实体类到索引库的映射。配置方法一般有两种,xml,annotation,这里主要介绍annotation的配置方法。

掌握五个注解:

@Searchable

@SearchableId

@SearchableProperty

@SearchableComponent

@SearchableReference

 

这里先讲讲@SearchableComponent和@SearchableReference,前三个注解请看后面的例子中的解释。

Compass中一个类对应一个document,而类中的符合属性如

@Searchable

Public class A{

       //@SearchableComponent

       //@SearchableReference

       privateB b;

}

类A和类B在数据库上有关联关系,在java类中,B是A的属性。而在compass中,也可以表示这种关联属性。

使用@SearchableReference标识类B的话,在将实体A存储到compass索引库中后,在查询到A的实体时,如果其对应的B实体也存在索引库中,则可以通过A的实体加载出索引库中的B。

 

@SearchableComponent标识类B的话,则compass会将B作为A的一部分,也就是创建document的时,B的数据元属性值也会被保存到A的document中,而且在进行索引查询时,如果要查询A实体的数据元属性中是否有某个关键字,也会对其对应的B中的数据元属性值进行索引,就算A中没有该关键字,而如果B中有该关键字,则也会把A作为命中对象。

但@SearchableReference只会通过A对B进行加载,不会在索引A的时候也去索引B。这一点要注意。

 

 

以Student类为例:

 

    @Entity //在hibernate中,使用@Entity(放在类的声明上)将Student类映射到数据库的student表

    @Searchable(alias="product") //在compass中,使用@Searchable(放在类的声明上)将Student映射到索引库中的product 文档(Document),alias用来指定该实体类对应的document的名称,不设置alias则默认使用小写的类名。如果searchable的root属性设为false,则该类不会被创建为一个单独的document。一般该类作为另外一个类的组件时,才设置该属性为false。这事要想通过该类的属性搜索该类就会报错。

     public class Student{

 

    //hibernate 和compass中都规定,实体必须有一个标识字段

   

    private intid;

       

   

       

        privateString name;

        privateString fond;

        privateTeacher teacher;

@Id //hiberante中,使用@Id(放在id属性的get方法上)来将一个字段映射为实体的标识ID

    @SearchableId //compass中,则使用@SearchableId(放在id属性的get方法上)将一个字段映射为实体的标识ID

        publicint getId(){

            returnid;

        }

 

//在hibernate中,对应属性如果不设置的话,则默认的映射为对应名称的字段

     //在compass中实体类的属性则需要使用@SearchableProperty(放在属性的get方法上)来设置,设置用来检索的属性,以及属性值是否被索引、是否被存储,以及相关度boost

     //@SearchableProperty中的name属性用来指定filed的名称

//index指定是否建立索引,Index.ANALYZE表示建立索引并进行分词(为默认值),即建立索引时会对该filed进行分词,并在检索的时候可以通过该filed进行搜索

//Index.NOT_ANALYZED表示会对该字段建立索引,但不会进行分词。

//Index.NO 表示不会对该字段建立索引,当然也不会进行分词了。

     //store表示该字段是否要被存储到索引库中,一般指定index=Index.ANALYZED都指定store=Store.YES(默认值),

//指定Index.NOT_ANALYZED并且指定Store.YES,则是为了搜索到实体时,将该filed值加载出来。

     //boost表示相关度,相关度是一个float型的值,值越大,通过字段匹配的结果排序越靠前

@SearchableProperty(name="name",index=Index.ANALYZED,store=Store.YES,boost=3f)

        publicString getName(){

            returnname;

        }

 

        publicString getFond(){

            returnfond;

        }

       

//@SearchableComponent复合类型,该类型的对象Teacher被看做是当前类Student的一部分,一个组件,创建document时,复合类型的属性会和student的属性放在同一个document中。

当进行查询时,会先查询主类的数据元字段,也会查询组件类如Teacher类中的字段,因为在创建document时,teacher的数据元字段作为student的一部分放到了document中。

@SearchableComponent

        public TeachergetTeacher(){

            returnteacher;

}

        public voidsetTeacher(Teacher teacher){

    this.teacher = teacher;

}

    }

 

//把root属性设为false,表示该类不是一个独立的实体,它只是其他类(Student)的一部分

@Searchable(root=false)

public class Teacher {

//此时该类的id就不能做为索引库中的标识了,因为该类被当做是Student的

//的一部分,而Student中已经有标识了。

@SearchableProperty(name="tid",index=Index.NO,store=Store.YES)

    private int id;

    //应为在创建索引时,该name对应的索引字段为name,而在Student中name对应的索引字段也为name,会造成冲突,所以需要将复合类型中与主类属性名字相同的

    //的,该为其他的的名称,以免冲突。

    @SearchableProperty(name="tname",index=Index.ANALYZED,store=Store.YES)

    private String name;

    @SearchableProperty(name="tage",index=Index.NO,store=Store.YES)

    private int age;

    @SearchableProperty(name="tfond",index=Index.ANALYZED,store=Store.YES)

    private String fond;

    //也就是说,查询到Teacher类的某个实体时,可以根据该实体加载出来其对应的student对象。

    @SearchableReference

    private Student student;

    //…………………………省略get/set方法 

}

3.使用Compass中的API对实体进行操作:

    Compasscompass = new CompassConfiguration().configure().bulidCompass();

    //compass 相当于 hibernate的sessionfactory

     //上面的configure()方法会自动去classpath找compass.cfg.xml配置文件,这个方法和hibernate中的configure()方法很相似,hibernate中的该方法用来加载hibernate.cfg.xml.

     //如果要制定其他名称的配置文件,可以使用configure(xxx).

// CompassSession 相当于hibernate中的Session

CompassSessioncompassSession = compass.openSession();

compassSession.beginTransaction(); // CompassTransaction 相当于 Hiberante中的Transaction,即compss中也是有事务支持的,和数据库很相似(也有事务回滚tx.rollback())

compassSession.create(entity)/.create(entity); //保存到索引文件

compassSession.delete(entity);//将实体从索引库中删除

compassSession.commit();

compassSession.close();

 

 

 

下面是测试时使用compass进行保存,查询等操作:

public class TestCompass {

    static Compass compass = null;

    @BeforeClass

    public static void beforeClass(){

       compass = new CompassAnnotationsConfiguration()

                     .setSetting(CompassEnvironment.CONNECTION, "file://index")

//                   .setSetting(CompassEnvironment.CONNECTION,"ram://index") //索引创建在内存中

                     .setSetting("compass.engine.highlighter.default.formatter.simple.pre", "<fontcolor='red'>")

                      .setSetting("compass.engine.highlighter.default.formatter.simple.post","</font>")

                     .addScan("com.wang.domain")

                     .buildCompass();    

    }

    @Test

    public void save(){

       CompassTransaction tx = null;

       CompassSession session = null;

       try {

           session = compass.openSession();

           tx = session.beginTransaction();

           Student st = new Student();

           st.setId(99);

           st.setAge(33);

           st.setName("凌飞");

           st.setFond("篮球飞");

           st.setSex("飞机");

           session.create(st);

//         session.save(st); //save和create方法是一样的。

           tx.commit();

       } catch (CompassException e) {

           tx.rollback();

           e.printStackTrace();

       } finally {

           if(null!=session&&!session.isClosed()){

              session.close();

            }

       }  

    }

    @Test

    public void getAll(){

       CompassTransaction tx = null;

       CompassSession session = null;

       try {

           session = compass.openSession();

           tx = session.beginTransaction();

//         如果直接find("楠"),则默认查找所有的索引filed(出去id标识之外的字段)。

//         CompassHits hits = session.find("楠");

          

//         如果要单在某个索引字段中查找,如只在name字段中查找,可以使用"name:楠"的形式。

//         CompassHits hits = session.find("name:楠");

          

//         如果要在多个filed中查找,则使用"name:楠 and fond:女" 或 "name:楠 + fond:女"。

//         compass在通过session.find查找到结果后,并不会直接把结果集保存到内存中(也就是hits),hits保存的只是结果记录在索引库中的索引号

//         在hits调用data(i)方法时,才会真正的返回指定索引号(i)的结果

           CompassHits hits = session.find("name:楠 and fond:女");

           List<Student> stuList = new ArrayList<Student>();

           for(int i = 0;i<hits.length();i++){

//            在hits调用data(i)方法时,才会真正的返回指定索引号(i)的结果

              Student st = (Student)hits.data(i);

              st.setName(hits.highlighter(i).fragment("name"));

              st.setFond(hits.highlighter(i).fragment("fond"));

              stuList.add(st);

           }

          

           for(Student st:stuList){

              System.out.println(st.getName()+"|"+st.getFond()+"|"+st.getId());

           }

           tx.commit();

       } catch (CompassException e) {

           tx.rollback();

           e.printStackTrace();

       } finally {

           if(null!=session&&!session.isClosed()){

              session.close();

           }

       }

    }

   

    //compass有保存,删除,但没有更新方法,要更新的话,可以先查询出来对象,然后删除对象,修改后再保存一次。

    @Test

    public void deleteAll(){

       CompassTransaction tx = null;

       CompassSession session = null;

       try {

           session = compass.openSession();

           tx = session.beginTransaction();

           CompassHits hits = session.find("name:楠 and fond:女");

           List<Student> stuList = new ArrayList<Student>();

           for(int i = 0;i<hits.length();i++){

              Student st = (Student)hits.data(i);

              session.delete(st);//删除

           }

          

           for(Student st:stuList){

              System.out.println(st.getName()+"|"+st.getFond()+"|"+st.getId());

           }

           tx.commit();

       } catch (CompassException e) {

           tx.rollback();

           e.printStackTrace();

       } finally {

           if(null!=session&&!session.isClosed()){

              session.close();

           }

       }

    }

}

 

Compass 与Spring的集成

在compass与spring的集成 和 hibernate与spring的集成也有很多相似之处:

1、 compass的事务也是交由spring管理的。

先思考一下compass不与spring集成时的情况。

我们将一个entity保存到数据库的同时,也要将该实体相关的数据信息保存到索引库中。所以代码常为一下情况:

Public  void saveStudent(Student stu){

        hibernateTeplate.save(stu);//保存到数据库中

      compassSession.save(stu);//保存到索引库中

}

我们有一点需要保证的是,必须在数据库保存成功后才能将实体对象保存到索引库中,然而在没有任何外界条件限制的情况先,数据库的事务和compass的事务是两个不同体系的事务,他们不可能自动的将事务并为一起。这样就无法保证整个操作的流程控制在同一个事务中,所以我们就要想办法来解决这个问题。

使用compass与spring集成,能很好的解决这个问题,spring会为hibernate与compass提供同一个事务管理,将整个操作流程(如上的saveStudent()方法)纳入同一个事务支持当中。

下面看一下配置中hibernate、compass、spring的集成:

其中hibernate与spring集成与前面完全一致,就是在后面加了一个compass的配置:

-------------下面就是 spring+JPA(Hiberante实现)+compass的集成----------

<aop:aspectj-autoproxy/>

<context:component-scan base-package="cn.itcast"/>  

<bean id="entityManagerFactory"class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

       <property name="persistenceUnitName" value="itcast"/>

</bean>  

<bean id="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory"ref="entityManagerFactory"/>

</bean>

  

<tx:annotation-driven transaction-manager="transactionManager"/>

<!—compass对象相当于 hibernate中的sessionFactory,在整个应用中时单例,这里使用org.compass.spring.LocalCompassBean,表示由spring来生成并管理compass对象,生成该对象需要一些属性设置。

 -->

<bean id="compass" class="org.compass.spring.LocalCompassBean">

    <!—配置compass的实体类映射-->

    <property name="classMappings">

       <list>

           <value>cn.itcast.bean.product.ProductInfo</value>

           <value>cn.itcast.bean.product.Brand</value>

           <value>cn.itcast.bean.product.ProductStyle</value>

           <value>cn.itcast.bean.product.ProductType</value>

       </list>

    </property>

    <!---compass相关的一些属性设置-->

    <property name="compassSettings">

       <props>

<!—- 指定compass的默认分词器,为:paoding分词,paoding分词器是基于字典的分词器,所以最好字典,使用paoding分词器时,将paoding分词器压缩目录下的字典目录(dic)放到项目的src目录下,并将paoding分词的paoding-dic-home.properties文件拷贝到src下,并在该配置文件中指定字典dic的路径为:classpath:dic。如果需要自定义一些字词,只需在dic目录下的字典文件中加上该词即可。-->

<prop key="compass.engine.analyzer.default.type">

               net.paoding.analysis.analyzer.PaodingAnalyzer

</prop>

<!—- compass索引库的位置 -->

<!-- file://e:/index 中以file://开头表示在硬盘上指定位置创建索引库-->

<!—  ram:// index 中以ram://开头表示在内存中创建索引库-->

           <prop key="compass.engine.connection">

file://e:/index

</prop>

<!—- 对关键字进行高亮(highlighter)设置 -->

           <prop key="compass.engine.highlighter.default。。。。pre">

<![CDATA[<font color='red'>]]>

</prop>

           <prop key="compass.engine.highlighter.。。。.simple.post">

<![CDATA[</font>]]>

</prop>

<!-- transaction.factory用于指定compass的事务工厂,这里指定为spring提供的事务工厂类,该工厂类会从spring中获得当前的事务,compass就可以使用当前事务了 -->

           <prop key="compass.transaction.factory">

org.compass.spring.transaction.SpringSyncTransactionFactory

</prop>

       </props>

    </property>

<!—-spring中的事务是由事务管理器来管理的,上面的指定的compass的transaction.factory要从spring获得当前事务,就必须去访问spring的事务管理器,这里就指定了compass的事务工厂要访问的spring的事务管理器,该事务管理器在前面已经配置好了,和hibernate那个一致,这里只需要引用就可以了-->

    <property name="transactionManager" ref="transactionManager" />

</bean>

    <!-- SingleCompassGps 类是compass的GPS机制的实现类,spring在实例化该类的对象后,会立即执行init-method属性指定的SingleCompassGps中的start()方法,start方法用于启动对 hibernate或JPA等持久化工具对实体类的增删改查的操作的监听捕获,因为hibernate或JPA等的持久化操作都会触发一些生命周期事件,而GPS可以对这些生命周期事件进行监听捕获,从而可以使用compass同步的将实体类信息更新到索引库中去-->

<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps"init-method="start" destroy-method="stop">

<!—GPS在监听到hibernate、JPA等对实体类的操作时,要使用compass对象将实体信息更新到索引库中,这里就是指出GPS要使用的compass对象,该compass对象在前面已经配置,这里只需要引用即可 -->

    <property name="compass" ref="compass"/>

    <property name="gpsDevices">

        <list>

<bean class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">

               <property name="gpsDevice" >

<!—GPS要监听与实体类对象相关的操作的生命周期,就必须知道是什么产品的工具在对实体类进行操作,这些工具可能是hibernate,JPA等,本项目使用的是JPA,所以要配置JPA相关的驱动,用来监听JPA操作实体时生命周期所发生的变化-->

              <bean class="org.compass.gps.device.jpa.JpaGpsDevice">

                    <!—- 这是设置的名字,随便取 -->

                  <property name="name" value="jpaDevice" />

<!—GPS要监听实体被操作的生命周期的变化,就需要去访问管理实体的工厂对象,这里指定entityManagerFactory 为前面配置的JPA提供的实体管理工厂-->

           <property name="entityManagerFactory"ref="entityManagerFactory" />

<!—        指定是否监听与实体相关的声明周期事件     -->

                  <property name="injectEntityLifecycleListener" value="true"/>

              </bean>

               </property>

           </bean>

        </list>

</property>

</bean>

 

-------------------------------compass+hibernate+spring---------------------

Compass+hibernate+spring的集成和前面的配置过程大致差不多,最后配置gps时是这样写的:

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值