Object-Directory Mapping (ODM)

对象关系映射框架(例如 Hibernate 和 JPA)使开发人员能够使用标注将关系数据库表映射到 Java 对象。Spring LDAP 项目通过 LdapOperations 中的多种方法,提供了关于 LDAP 目录的类似功能:

Object-relational mapping frameworks (such as Hibernate and JPA) offer developers the ability to use annotations to map relational database tables to Java objects. The Spring LDAP project offers a similar ability with respect to LDAP directories through a number of methods in 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)

Annotations

使用对象映射方法管理的实体类需要使用 org.springframework.ldap.odm.annotations 包中的标注进行标注。可用的标注有:

Entity classes managed with the object mapping methods are required to be annotated with annotations from the org.springframework.ldap.odm.annotations package. The available annotations are:

  • @Entry: Class level annotation indicating the objectClass definitions to which the entity maps._ (required)_

  • @Id: Indicates the entity DN. The field declaring this attribute must be a derivative of the javax.naming.Name class. (required)

  • @Attribute: Indicates the mapping of a directory attribute to the object class field.

  • @DnAttribute: Indicates the mapping of a DN attribute to the object class field.

  • @Transient: Indicates the field is not persistent and should be ignored by the OdmManager.

必需在受管理类上声明 @Entry@Id 标注。@Entry 用于指定实体映射到的对象类,以及 LDAP 条目目录根(可选)。需要声明所有为此映射字段的对象类。请注意,在创建受管理类的新条目时,仅使用已声明的对象类。

The @Entry and @Id annotations are required to be declared on managed classes. @Entry is used to specify which object classes the entity maps to and (optionally) the directory root of the LDAP entries represented by the class. All object classes for which fields are mapped are required to be declared. Note that, when creating new entries of the managed class, only the declared object classes are used.

为了使目录条目被视为受管理实体的匹配项,目录条目声明的所有对象类都必须由 @Entry 标注声明。例如,假设您的 LDAP 树中有具有以下对象类的条目:inetOrgPerson、organizationalPerson、person、top。如果您仅对更改 person 对象类中定义的属性感兴趣,则可以使用 @Entry(objectClasses = { "person", "top" })@Entry 标注。但是,如果您想管理 inetOrgPerson 对象类中定义的属性,则需要使用以下内容:@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })

In order for a directory entry to be considered a match to the managed entity, all object classes declared by the directory entry must be declared by the @Entry annotation. For example, assume that you have entries in your LDAP tree that have the following object classes: inetOrgPerson,organizationalPerson,person,top. If you are interested only in changing the attributes defined in the person object class, you can annotate your @Entry with @Entry(objectClasses = { "person", "top" }). However, if you want to manage attributes defined in the inetOrgPerson objectclass, you need to use the following: @Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" }).

所有实体字段都通过其字段名称映射到 LDAP 属性。其余标注(@Id@Attribute@Transient@DnAttribute)影响此映射的发生方式。

All entity fields are mapped by their field name to LDAP attributes. The remaining annotations — @Id, @Attribute, @Transient, and @DnAttribute — affect how that mapping occurs.

首先,@Id 标注将条目的专有名称映射到字段。该字段必须是 javax.naming.Name 的实例。

First, the @Id annotation maps the distinguished name of the entry to a field. The field must be an instance of javax.naming.Name.

其次,@Attribute 标注将实体字段映射到 LDAP 属性。当属性名称与字段名称不同时,这很方便。若要使用 @Attribute,您必须声明该字段映射到的属性的名称。另外,您还可以通过包含 LDAP 属性的语法 OID 来保证精确匹配。最后,@Attribute 还提供类型声明,通过该类型声明,您可以指示 LDAP JNDI 提供程序是否将属性视为基于二进制还是基于字符串的。

Second, the @Attribute annotation maps entity fields to LDAP attributes. This is handy when the attribute name is different from the field name. To use @Attribute, you must declare the name of the attribute to which the field maps. Optionally, you can also guarantee and exact match by including the syntax OID of the LDAP attribute. Finally, @Attribute also provides the type declaration, which lets you indicate whether the attribute is regarded as binary- or string-based by the LDAP JNDI provider.

第三,@Transient 标注指示给定的实体字段不映射到 LDAP 属性。

Third, the @Transient annotation indicates that the given entity field does not map to an LDAP attribute.

最后,@DnAttribute 标注还可以将实体字段映射到条目的专有名称的组件。只有 String 类型的字段才能使用 @DnAttribute 标注。不支持其他类型。

Finally, the @DnAttribute annotation additionally maps entity fields to components of an entry’s distinguished name. Only fields of type String can be annotated with @DnAttribute. Other types are not supported.

当指定类中所有 @DnAttribute 注解的 index 属性时,在创建和更新条目时也可以自动计算 DN。对于更新场景,如果与识别名称相关联的属性发生更改,这还会自动在树中移动条目。

When the index attribute of all @DnAttribute annotations in a class is specified, the DN can also be automatically calculated when creating and updating entries. For update scenarios, this also automatically takes care of moving entries in the tree if attributes that are part of the distinguished name have changed.

记住所有字段都默认映射到 LDAP 属性。@DnAttribute 不会更改此设置;换句话说,除非您还用 @Transient 注解字段,否则用 @DnAttribute 注解的字段也将映射到 LDAP 属性。

Remember that all fields are mapped to LDAP attributes by default. @DnAttribute does not change this; in other words, fields annotated with @DnAttribute will also map to an LDAP attribute, unless you also annotate the field with @Transient.

Execution

当所有组件都已正确配置并进行标注时,可以使用 LdapTemplate 的对象映射方法,如下所示:

When all components have been properly configured and annotated, the object mapping methods of LdapTemplate can be used as follows:

Example 1. Execution
@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;
   // ...more attributes below
}


public class OdmPersonRepo {
   @Autowired
   private LdapTemplate ldapTemplate;

   public Person create(Person person) {
      ldapTemplate.create(person);
      return person;
   }

   public Person findByUid(String uid) {
      return ldapTemplate.findOne(query().where("uid").is(uid), Person.class);
   }

   public void update(Person person) {
      ldapTemplate.update(person);
   }

   public void delete(Person person) {
      ldapTemplate.delete(person);
   }

   public List<Person> findAll() {
      return ldapTemplate.findAll(Person.class);
   }

   public List<Person> findByLastName(String lastName) {
      return ldapTemplate.find(query().where("sn").is(lastName), Person.class);
   }

   public Stream<Person> streamFindByLastName(String lastName) {
      return ldapTemplate.findStream(query().where("sn").is(lastName), Person.class);
   }
}

ODM and Distinguished Names as Attribute Values

LDAP 中的安全组通常包含一个多值属性,其中每个值都是系统中用户的 distinguished name。有关处理此类属性时的困难已在 xref:dirobjectfactory.adoc#dns-as-attribute-values[DirContextAdapter 和 Distinguished Names as Attribute Values 中进行了解释。

Security groups in LDAP commonly contain a multi-value attribute, where each of the values is the distinguished name of a user in the system. The difficulties involved when handling these kinds of attributes are discussed in DirContextAdapter and Distinguished Names as Attribute Values.

ODM 还支持 javax.naming.Name 属性值,这使得组修改变得容易,如下例所示:

ODM also has support for javax.naming.Name attribute values, making group modifications easy, as the following example shows:

Example 2. Example Group representation
@Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=groups")
public class Group {

    @Id
    private Name dn;

    @Attribute(name="cn")
    @DnAttribute("cn")
    private String name;

    @Attribute(name="uniqueMember")
    private Set<Name> members;

    public Name getDn() {
        return dn;
    }

    public void setDn(Name dn) {
        this.dn = dn;
    }

    public Set<Name> getMembers() {
        return members;
    }

    public void setMembers(Set<Name> members) {
        this.members = members;
    }

    public String getName() {
        return name;
    }

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

    public void addMember(Name member) {
        members.add(member);
    }

    public void removeMember(Name member) {
        members.remove(member);
    }
}

当您使用 setMembersaddMemberremoveMember 修改组成员,然后调用 ldapTemplate.update() 时,属性修改是通过使用专有名称相等性计算的,这意味着在计算它们是否相等时不考虑专有名称的文本格式。

When you modify group members by using setMembers, addMember, and removeMember and then calling ldapTemplate.update(), attribute modifications are calculated by using distinguished name equality, meaning that the text formatting of distinguished names is disregarded when figuring out whether they are equal.