iBatis高级特性

1 缓存
1.1 缓存代码示例
1)主配置文件打开缓存开关

<settings cacheModelsEnabled="true"/>

2)sqlmap配置文件
<sqlMap namespace="person">
    <typeAlias alias="person" type="org.frank1234.ibatis.helloworld.Person"/>
    <cacheModel id="personCache" type="LRU">
               <property name="size" value="100"/>
    </cacheModel>
    <select id="getPerson" resultClass="person" cacheModel="personCache">
        select id,name,sex,age from person where id=#value#
    </select>
    </sqlMap>

3)执行2次查询则会发现输出日志有如下:
2015-01-02 15:32:21,979: Cache 'person.personCache': cache miss
2015-01-02 15:32:22,218: {conn-100000} Connection
2015-01-02 15:32:22,229: {conn-100000} Preparing Statement:          select id,name,sex,age from person where id=?    
2015-01-02 15:32:22,256: {pstm-100001} Executing Statement:          select id,name,sex,age from person where id=?    
2015-01-02 15:32:22,256: {pstm-100001} Parameters: [2]
2015-01-02 15:32:22,256: {pstm-100001} Types: [java.lang.Integer]
2015-01-02 15:32:22,260: {rset-100002} ResultSet
2015-01-02 15:32:22,276: {rset-100002} Header: [id, name, sex, age]
2015-01-02 15:32:22,276: {rset-100002} Result: [2, frank1234, 男, 80]
2015-01-02 15:32:22,276: Cache 'person.personCache': stored object 'id=2,name=frank1234,age=80,sex=男'
id=2,name=frank1234,age=80,sex=男
2015-01-02 15:32:22,277: Cache 'person.personCache': retrieved object 'id=2,name=frank1234,age=80,sex=男'
id=0,name=null,age=0,sex=null

由输出可见,查询时先检查缓存中是否存在,第一次查询缓存中不存在,输出:Cache 'person.personCache': cache miss
然后从数据库中获取,获取后将对象缓存起来。输出:Cache 'person.personCache': stored object 'id=2,name=frank1234,age=80,sex=男'。
第二次查询由于查询的是同一个对象,缓存直接命中,不再访问数据库。输出:Cache 'person.personCache': retrieved object 'id=2,name=frank1234,age=80,sex=男'

1.2 使用缓存注意事项
1)一定要配置好flushOnExecute,使缓存中的脏数据失效,否则性能提升了,但是获取的是错误的数据对象,还不如不通过缓存提升性能呢。
2)注意直接操作sql造成的缓存失效
3)注意第三方系统操作数据造成的缓存失效。
4)集群部署造成的缓存失效。比如第一次查询走集群的节点A,删除操作走的是集群的节点B,这样节点A的缓存就是脏数据了。
所以缓存的使用要小心为妙,小心使得万年船。另外注意缓存使用的普世原则,即缓存适合读多写少,以及存在热点数据的场景。

1.3 其他配置参数
<cacheModel id="personCache" type="LRU" serialize="true" readOnly="true">
    <flushInterval hours="24"/>
    <flushOnExecute statement="updatePerson"/>
    <property name="size" value="100"/>
</cacheModel>


1)serialize="true":是否使用全局的数据缓存,如果设置为false的话,则只对当前session有效。
2)readOnly="true" : 如果设置为true,则只要缓存中的数据对象的属性发生了变化,则此数据对象就从缓存中移除。
public static void get() throws Exception{
        SqlMapClient client = IBatisUtil.getSqlMap();
        Person person = new Person();
        client.queryForObject("getPerson",new Integer(2),person);
        person.setName("haha");
//        System.out.println(person);
    }
设置成true和false两次执行get()方法的输出差别:
设置成true,输出:
2015-01-02 15:54:07,245: Cache 'person.personCache': stored object 'id=2,name=frank1234,age=80,sex=男'
2015-01-02 15:54:07,245: Cache 'person.personCache': retrieved object 'id=2,name=haha,age=80,sex=男'
可见第二次get()从缓存中获取的name值变为了haha.
设置成false,输出:
2015-01-02 15:55:04,803: Cache 'person.personCache': stored object '[B@2e1f1f'
2015-01-02 15:55:04,806: Cache 'person.personCache': retrieved object 'id=2,name=frank1234,age=80,sex=男'
可见第二次get()从缓存中获取的name值仍然是frank1234

3)<flushInterval hours="24"/> :多长时间清除缓存,示例代表24小时强行清除缓存,如果不设置则代表不强行清除。
4)<flushOnExecute statement="updatePerson"/>:执行指定的statement时,将此cacheModel的缓存清空,请注意是全部清空此cacheModel。
比如先query的时候query的是id=2的Person对象,updatePerson修改的是id=3的Person对象,然后query id=2的对象,依然会查不到,因为执行updatePerson的时候整个chacheModel被清空了。
5)type取值:type可以设置为MEMORY/LRU/FIFO/OSCACHE。
MEMORY:
<cacheModel id="personCache" type="MEMORY" serialize="true" readOnly="false">
    <flushInterval hours="24"/>
    <flushOnExecute statement="updatePerson"/>
    <property name="reference-type" value="STRONG"/>
</cacheModel>
value可以是STRONG/SOFT/WEAK。
分别对应Java的强引用/软引用和弱引用。
强引用代表只要对象没死宁可撑爆内存也不回收。
软引用代表垃圾回收的时候如果JVM内存不够用了,则回收软引用对象,否则不回收。
弱引用代表垃圾回收的时候不管JVM内存够不够用都回收。

LRU对应的有size属性,是对象满的时候清谁出去的算法,LRU代表最近最少使用的对象被干掉。
FIFO代表先进先出。

OSCACHE是集成第三方缓存实现。





2 关联查询
2.1 1:1关联查询
比如丈夫和妻子是1:1关系,天朝历来实行一夫一妻制,古代皇帝也是一夫一妻制,因为皇后只有一个,多会玩概念啊。
数据库表创建并构造数据:
create table husband (id varchar(10) not null,name varchar(20) not null,primary key (id));
create table wife (id varchar(10) not null,name varchar(20) not null,husbandid varchar(10) not null,primary key (id));

insert into husband(id,name) values('1','frank1234');
insert into wife(id,name,husbandid) values('1','frank12345',1);

Husband对象:
public class Husband implements Serializable{
    public Husband(){}
    private String id;
    private String name;
    private String wifeId;
    private String wifeName;
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWifeId() {
        return wifeId;
    }

    public void setWifeId(String wifeId) {
        this.wifeId = wifeId;
    }

    public String getWifeName() {
        return wifeName;
    }

    public void setWifeName(String wifeName) {
        this.wifeName = wifeName;
    }
    public String toString(){
        return "id="+id+",name="+name+",wifeid="+wifeId+",wifename="+wifeName;
    }
}


SqlMap配置文件:
<select id="getHusband" parameterClass="java.lang.String" resultClass="org.frank1234.ibatis.helloworld.Husband">
    select a.id ,a.name ,b.id as wifeId,b.name as wifeName from husband a join wife b on a.id=b.husbandid
    where a.id=#value#
</select>


查询代码:
SqlMapClient client = IBatisUtil.getSqlMap();
Husband husband = (Husband)client.queryForObject("getHusband","1");
System.out.println(husband);


输出结果:
id=1,name=frank1234,wifeid=1,wifename=frank12345

当然如果查询出有多条记录,也就说1个husband对应n个wife的场景,一夫多妻制了哈哈,男人们的梦想啊。
可以通过:
SqlMapClient client = IBatisUtil.getSqlMap();
List list = client.queryForList("getHusband","1");
for(int i=0;i<list.size();i++){
    System.out.println((Husband)list.get(i));
}
输出:
id=1,name=frank1234,wifeid=1,wifename=frank12345
id=1,name=frank1234,wifeid=2,wifename=wife2

但是请注意,Husband对象始终是1:1的,如果要在Husband中添加一个List<Wife>属性,则这种方式就办不到了,得往下看。

2.2 1:n 关联查询
比如这个男人有钱有权了,脱贫致富进入了包二奶模式,即1:n关系。各位看官,此比喻存属玩笑,如是这样渣男一个,鉴定完毕。
数据库表创建并构造数据:
create table man (id varchar(10) not null,name varchar(20) not null,primary key (id));
create table lover (id varchar(10) not null,name varchar(20) not null,manid varchar(10) not null,primary key (id));

insert into man(id,name) values('1','frank1234');
insert into lover(id,name,manid) values('1','beauty1',1);
insert into lover(id,name,manid) values('2','beauty2',1);

Man对象:
public class Man implements Serializable {
    private String id;
    private String name;
    private List<Lover> lovers;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Lover> getLovers() {
        return lovers;
    }

    public void setLovers(List<Lover> lovers) {
        this.lovers = lovers;
    }
    public String toString(){
        return "id="+id+",name="+name;
    }
}

不同的是其中的lovers属性是一个List对象。

Lover对象:
public class Lover implements Serializable{
    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public String toString(){
        return "id="+id+",name="+name;
    }
}


sqlMap配置文件:

<resultMap id="get-man-result" class="org.frank1234.ibatis.helloworld.Man">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="lovers" column="id" select="getLoversByManId"/>
</resultMap>
<select id="getLoversByManId" parameterClass="java.lang.String" resultClass="org.frank1234.ibatis.helloworld.Lover">
    select * from lover where manid=#value#
</select>
<select id="getMan" parameterClass="java.lang.String" resultMap="get-man-result">
   select * from man where id=#value#
</select>

通过resultMap来关联查询另外一个sql。
<result property="lovers" column="id" select="getLoversByManId"/>其中select属性关联另外一个statement。column属性值对应的是man表中的字段,即用哪个字段的值去lover表中查询,即用哪个值传递给getLoversByManId这个Statement。

测试代码及输出结果:
SqlMapClient client = IBatisUtil.getSqlMap();
Man man = (Man)client.queryForObject("getMan","1");
List list = man.getLovers();
for(int i=0;i<list.size();i++){
    System.out.println(man+"->>>>"+(Lover)list.get(i));
}

输出结果:
id=1,name=frank1234->>>>id=1,name=beauty1
id=1,name=frank1234->>>>id=2,name=beauty2



对于n:1方,例如Lover中要添加个Man的属性,也可以解决,代码如下:
Lover:
public class Lover implements Serializable{
    private String id;
    private String name;
    private Man man;
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Man getMan() {
        return man;
    }

    public void setMan(Man man) {
        this.man = man;
    }

    public String toString(){
        return "id="+id+",name="+name;
    }
}


sqlmap配置文件如下:
<select id="getMan" parameterClass="java.lang.String" resultMap="get-man-result">
    select * from man where id=#value#
</select>
<resultMap id="get-lover-result" class="org.frank1234.ibatis.helloworld.Lover">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="man" column="manid" select="getMan"/>
</resultMap>
<select id="getLover" parameterClass="java.lang.String" resultMap="get-lover-result">
    select * from lover where id=#value#
</select>


测试代码及输出如下:
SqlMapClient client = IBatisUtil.getSqlMap();
Lover lover = (Lover)client.queryForObject("getLover","1");
System.out.println(lover.getMan());

输出:
id=1,name=frank1234



3 延迟加载
<settings  lazyLoadingEnabled="true"     />
设置为true代表打开延迟加载开关。

例如对于之前1:n的关联查询。
SqlMapClient client = IBatisUtil.getSqlMap();
Man man = (Man)client.queryForObject("getMan","1");
List list = man.getLovers();
for(int i=0;i<list.size();i++){
    System.out.println(man+"-》》》》"+(Lover)list.get(i));
}


如果lazyLoadingEnabled=false,则
执行Man man = (Man)client.queryForObject("getMan","1");的同时也执行select * from lover where manid=?  将lovers属性构造出来。如果用不着的话,就白白消耗了,所以最好别这样。

将lazyLoadingEnabled=true,执行List list = man.getLovers();的时候都不执行sql select * from lover where manid=?,只有在执行他的时候System.out.println(man+"-》》》》"+(Lover)list.get(i));,才执行上面的sql,也就是只有真正用到的时候才加载。

所以建议这个属性始终设置为true。


猜你喜欢

转载自frank1234.iteye.com/blog/2171823