Spring ldap ODM

Spring ldap ODM

上文讲述了Spring-ldap基本操作,通过定义LdapTemplate这个bean到IOC容器,使用时注入LdapTemplate即可完成对LDAP目录树的CRUD及筛选、过滤等。

  • 但是对于筛选查询出来的内容,JNDI是封装在Attributes中,尽管spring-ldap提供了AttributesMapper接口,让你自己去实现具体的从Attributes转成Java对象的逻辑,但是随着业务变化,对于不同的查询列,转化逻辑必须重新写。

  • 那有没有办法,让查询出来的内容,自动转成我们想要Java对象呢,就像关系型数据库ORM,把从MySQL数据库查询的结果集,自动完成和实体类的映射,执行查询时,直接得到对象列表。

  • spring-ldap同样提供相应的支持(从spring-ldap 2.x版本开始),ODM (Object-Directory Mapping)对象目录映射。不同于MySQL数据库的是,LDAP是目录树,是树结构的数据库。

spring-ldap该框架通过提供和ORM中相似的机制对LDAP相关操作进行封装,主要包括:
1、类比SessionFactory的LdapContextSource;
2、类比HibernateTemplate等的LdapTemplate;
3、伪事务支持,能否与tx框架的TransactionManager混用未知;
4、类比JPA的使用@Entry、@Attribute、@Id标注的注解。

ORM框架,例如Hibernate 或者JPA,使得我们可以使用注解,映射一张表到一个Java实体。Spring-ldap提供相似的功能以完成对LDAP目录树的操作,LdapOperations接口提供了很多类似的方法:

<T> T findByDn(Name dn, Class<T> clazz)

<T> T findOne(LdapQuery query, Class<T> clazz)

<T> List<T> find(LdapQuery query, Class<T> clazz)

<T> List<T> findAll(Class<T> clazz)

<T> List<T> findAll(Name base, SearchControls searchControls, Class<T> clazz)

<T> List<T> findAll(Name base, Filter filter, SearchControls searchControls, Class<T> clazz)

void create(Object entry)
void update(Object entry)
void delete(Object entry)

LdapTemplate实现了该接口,这些方法均可在完成LdapTemplate bean定义后,注入使用。首先按完成基本工程搭建http://www.jianshu.com/p/3aeb49a9befd

ODM注解

需要完成映射的实体类需要使用注释。由org.springframework.ldap.odm.annotations package提供。

  • @Entry - 用于标注实体类(required)
  • @Id - 指明实体DN; 是javax.naming.Name类型(required)
  • @Attribute - 标识实体类需要映射的字段
  • @DnAttribute -
  • @Transient - 标识实体类不需要映射的字段
示例:
@Entry(objectClasses = { "person", "top" }, base="ou=someOu")
public class Person {
   @Id
   private Name dn;

   @Attribute(name="cn")
   @DnAttribute(value="cn", index=1)
   private String fullName;

   // No @Attribute annotation means this will be bound to the LDAP attribute
   // with the same value
   private String description;

   @DnAttribute(value="ou", index=0)
   @Transient
   private String company;

   @Transient
   private String someUnmappedField;
}

@Entry标记,其中的objectClasses定义必须与objectClass完全一致,并且可以指定多个值。在新建和查询object时,ODM会根据此标记进行匹配,无需再指定objectClass。

每个entry必须指定@Id字段,类型为javax.naming.Name,其实就是DN。但是若在LdapContextSource中指定了base,则DN将会按照base截取相对路径。比如,DN为cn=user,ou=users,dc=jayxu,dc=com,base为dc=jayxu,dc=com,则取出的user对象DN为cn=user,ou=users。

对于不需要与LDAP进行映射的字段使用@Transient进行标记

ODM操作

    /**
     * 按部门编号 o 查询部门
     */
    @Test
    public void findOne(){
        LdapQuery ldapQuery = query().where("o").is("039cb846de0e40e08901e85293f642bf");
        LdapDept dept = ldapTemplate.findOne(ldapQuery, LdapDept.class);
        System.out.println(dept);
    }

    /**
     * 按条件过滤部门
     */
    @Test
    public void filter(){
        LdapQueryBuilder ldapQueryBuilder = query();
        Filter filter = new GreaterThanOrEqualsFilter("modifyTimestamp",timestamp);
        ldapQueryBuilder.filter(filter);
        List<LdapDept> depts = ldapTemplate.find(ldapQueryBuilder, LdapDept.class);
        for (LdapDept dept: depts ) {
            System.out.println(dept);
        }
    }

    /**
     * 查询所有部门
     * 类型和base,由LdapDept上的@Entry指定
     */
    @Test
    public void getAllDepts(){
        List<LdapDept> depts = ldapTemplate.findAll(LdapDept.class);
        for (LdapDept dept: depts ) {
            System.out.println(dept);
        }
    }

其中LdapDept是自定义实体类,按照Entry、@Attribute、@Id注解的约定标注好即可。

使用org.springframework.ldap.pool.factory.PoolingContextSource引入连接池

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.pool.factory.PoolingContextSource;
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;

import java.util.HashMap;
import java.util.Map;

/**
 * LDAP 的自动配置类
 *
 * 完成连接LDAP 及LdapTemplate 的定义
 */
@ConfigurationProperties(prefix = "ldap")
@PropertySource("classpath:/application.yml")
@Configuration
public class LdapConfiguration {

    private LdapTemplate ldapTemplate;

    @Value("${maxActive}")
    private int maxActive;

    @Value(value = "${maxTotal}")
    private int maxTotal;

    @Value(value = "${maxIdle}")
    private int maxIdle;

    @Value(value = "${minIdle}")
    private int minIdle;

    @Value(value = "${maxWait}")
    private int maxWait;

    @Value(value = "${url}")
    private String LDAP_URL;

    @Value(value = "${base}")
    private String BASE_DC;

    @Value(value = "${dbusername}")
    private String USER_NAME;

    @Value(value = "${password}")
    private String PASS_WORD;

    @Bean
    public LdapContextSource contextSource() {
        LdapContextSource contextSource = new LdapContextSource();
        Map<String, Object> config = new HashMap();

        contextSource.setUrl(LDAP_URL);
        contextSource.setBase(BASE_DC);
        contextSource.setUserDn(USER_NAME);
        contextSource.setPassword(PASS_WORD);

        //  解决 乱码 的关键一句
        config.put("java.naming.ldap.attributes.binary", "objectGUID");

        //当需要连接时,池是否一定创建新连接
        contextSource.setPooled(true);
        contextSource.setBaseEnvironmentProperties(config);
        return contextSource;
    }

    /**
     * LDAP pool 配置
     * @return
     */
    @Bean
    public ContextSource poolingLdapContextSource() {
        PoolingContextSource poolingContextSource = new PoolingContextSource();
        poolingContextSource.setDirContextValidator(new DefaultDirContextValidator());
        poolingContextSource.setContextSource(contextSource());
        poolingContextSource.setTestOnBorrow(true);//在从对象池获取对象时是否检测对象有效
        poolingContextSource.setTestWhileIdle(true);//在检测空闲对象线程检测到对象不需要移除时,是否检测对象的有效性

        poolingContextSource.setMaxActive(maxActive <= 0 ? 20:maxActive);
        poolingContextSource.setMaxTotal(maxTotal <= 0 ? 40:maxTotal);
        poolingContextSource.setMaxIdle(maxIdle <= 0 ? 5:maxIdle);
        poolingContextSource.setMinIdle(minIdle <= 0 ? 5:minIdle);
        poolingContextSource.setMaxWait(maxWait <= 0 ? 5:maxWait);

        TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(poolingContextSource);
        return proxy;
    }

    @Bean
    public LdapTemplate ldapTemplate() {
        if (null == ldapTemplate)
            ldapTemplate = new LdapTemplate(poolingLdapContextSource());
        return ldapTemplate;
    }
}

ODM官方文档
https://docs.spring.io/spring-ldap/docs/2.3.2.RELEASE/reference/#odm

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容