anchor.diff:  *  The ODM (Object-Directory Mapping) functionality has been moved to core and there are new methods in `LdapOperations`/`LdapTemplate` that uses this automatic translation to/from ODM-annotated classes. See <<odm>> for more information.
   *  A custom XML namespace is now provided to simplify configuration of Spring LDAP. See <<configuration>> for more information.
   *  Spring Data Repository and QueryDSL support is now included in Spring LDAP. See <<repositories>> for more information.
  +*  `Name` instances as attribute values are now handled properly with regards to Distinguished Name equality in `DirContextAdapter` and ODM. See <<dns-as-attribute-values>> and <<odm-dn-attributes>> for more information.
   * `DistinguishedName` and associated classes have been deprecated in favor of standard Java `LdapName`. See <<ldap-names>> for information on how the library helps working with `LdapNames`.
   *  Fluent LDAP query support has been added. This makes for a more pleasant programming experience when working with LDAP searches in Spring LDAP. See <<basic-queries>> and <<query-builder-advanced>> for more information about the LDAP query builder support.
   *  The old `authenticate` methods in `LdapTemplate` have been deprecated in favor of a couple of new `authenticate` methods that work with `LdapQuery` objects and __throw exceptions__ on authentication failure, making it easier for the user to find out what caused an authentication attempt to fail.
  
positive.code:       return ldapTemplate.findAll(Person.class);
     }
  
     public List>Person< findByLastName(String lastName) {
        return ldapTemplate.find(query().where("sn").is(lastName), Person.class);
     }
  }
  ----
  
  
  [[query-builder-advanced]]
  == Advanced LDAP Queries
  
negative.code: }
  ----
  
  [[dns-as-attribute-values]]
  === DirContextAdapter and Distinguished Names as Attribute Values.
  
  When managing security groups in LDAP it is very common to have attribute values that are actually
  distinguished names. Since distinguished name equality is not the same as String equality, handling these
  attributes as normal strings when calculating attribute modifications will not work as expected. For instance,
  if a `member` attribute has the value `cn=John Doe,ou=People` and we call `ctx.addAttributeValue("member", "CN=John Doe, OU=People")`,
  the attribute will now be considered to have two values, even though the strings actually represent the same
  distinguished name.
  
  As of version 2.0, if you supply `javax.naming.Name` instances to the attribute modification methods in `DirContextAdapter`,
  modification calculation will use distinguished name equality, meaning that if we modify the example above to:
  `ctx.addAttributeValue("member", LdapUtils.newLdapName("CN=John Doe, OU=People"))`, this will no longer be considered
  a modification.
  
  .Group membership modification example
  [source,java]
  [subs="verbatim,quotes"]
  ----
  public class GroupDao implements BaseLdapNameAware {
      private LdapTemplate ldapTemplate;
      private LdapName baseLdapPath;
  
      public void setLdapTemplate(LdapTemplate ldapTemplate) {
          this.ldapTemplate = ldapTemplate;
      }
  
      public void setBaseLdapPath(LdapName baseLdapPath) {
          this.setBaseLdapPath(baseLdapPath);
      }
  
      public void addMemberToGroup(String groupName, Person p) {
          Name groupDn = buildGroupDn(groupName);
          Name userDn = buildPersonDn(
              person.getFullname(),
              person.getCompany(),
              person.getCountry());
  
          DirContextOperation ctx = ldapTemplate.lookupContext(groupDn);
          ctx.addAttributeValue("member", userDn);
  
          ldapTemplate.update(ctx);
      }
  
      public void removeMemberFromGroup(String groupName, Person p) {
          Name groupDn = buildGroupDn(String groupName);
          Name userDn = buildPersonDn(
              person.getFullname(),
              person.getCompany(),
              person.getCountry());
  
          DirContextOperation ctx = ldapTemplate.lookupContext(groupDn);
          ctx.removeAttributeValue("member", userDn);
  
          ldapTemplate.update(ctx);
      }
  
      private Name buildGroupDn(String groupName) {
          return LdapNameBuilder.newLdapName("ou=Groups")
              .add("cn", groupName).build();
      }
  
      private Name buildPersonDn(String fullname, String company, String country) {
          return LdapNameBuilder.newLdapName(baseLdapPath)
              .add("c", country)
              .add("ou", company)
              .add("cn", fullname)
              .build();
     }
  }
  ----
  
  In the example above we are implementing `BaseLdapNameAware`, in order to get hold of the base LDAP path as described in <<base-context-configuration>>.
  This is necessary because distinguished names as member attribute values must always be absolute from the directory root.
  
  === A Complete PersonDao Class
  To illustrate the power of Spring LDAP, here is a complete Person DAO implementation for LDAP in just 68 lines:
  