学习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操作实体(application programming Interface)
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
private B 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
public intgetId(){
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
CompassSession compassSession =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", "")
.setSetting("compass.engine.highlighter.default.formatter.simple.post","")
.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
for(int i = 0;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 for(int i = 0;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"/> 对象相当于 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.ProductInfovalue> <value>cn.itcast.bean.product.Brandvalue> <value>cn.itcast.bean.product.ProductStylevalue> <value>cn.itcast.bean.product.ProductTypevalue> list> property> <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> 索引库的位置 -->
中以ram://开头表示在内存中创建索引库--> <prop key="compass.engine.connection"> file://e:/index prop> 对关键字进行高亮(highlighter)设置 --> <prop key="compass.engine.highlighter.default。。。。pre"> ]]> prop> <prop key="compass.engine.highlighter.。。。.simple.post"> ]]> prop>
<prop key="compass.transaction.factory"> org.compass.spring.transaction.SpringSyncTransactionFactory prop> props> property> 中的事务是由事务管理器来管理的,上面的指定的compass的transaction.factory要从spring获得当前事务,就必须去访问spring的事务管理器,这里就指定了compass的事务工厂要访问的spring的事务管理器,该事务管理器在前面已经配置好了,和hibernate那个一致,这里只需要引用就可以了--> <property name="transactionManager" ref="transactionManager" /> bean> <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" > 要监听与实体类对象相关的操作的生命周期,就必须知道是什么产品的工具在对实体类进行操作,这些工具可能是hibernate,JPA等,本项目使用的是JPA,所以要配置JPA相关的驱动,用来监听JPA操作实体时生命周期所发生的变化--> <bean class="org.compass.gps.device.jpa.JpaGpsDevice"> 这是设置的名字,随便取 --> <property name="name" value="jpaDevice" /> 要监听实体被操作的生命周期的变化,就需要去访问管理实体的工厂对象,这里指定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时是这样写的: