diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
index 769b80f25..bbfae15f0 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
@@ -1998,7 +1998,10 @@ class ClassMetadataInfo implements ClassMetadata
      *
      * @param string $columnName
      *
-     * @return \Doctrine\DBAL\Types\Type
+     * @return \Doctrine\DBAL\Types\Type|string|null
+     *
+     * @deprecated this method is bogous and unreliable, since it cannot resolve the type of a column that is
+     *             derived by a referenced field on a different entity.
      */
     public function getTypeOfColumn($columnName)
     {
diff --git a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php
index 40b1be958..a421b9112 100644
--- a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php
@@ -25,6 +25,7 @@ use Doctrine\ORM\Persisters\SqlExpressionVisitor;
 use Doctrine\ORM\Persisters\SqlValueVisitor;
 use Doctrine\ORM\PersistentCollection;
 use Doctrine\ORM\Query;
+use Doctrine\ORM\Utility\PersisterHelper;
 
 /**
  * Persister for many-to-many collections.
@@ -61,15 +62,23 @@ class ManyToManyPersister extends AbstractCollectionPersister
             return; // ignore inverse side
         }
 
-        $insertSql = $this->getInsertRowSQL($collection);
-        $deleteSql = $this->getDeleteRowSQL($collection);
+        list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection);
+        list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection);
 
         foreach ($collection->getDeleteDiff() as $element) {
-            $this->conn->executeUpdate($deleteSql, $this->getDeleteRowSQLParameters($collection, $element));
+            $this->conn->executeUpdate(
+                $deleteSql,
+                $this->getDeleteRowSQLParameters($collection, $element),
+                $deleteTypes
+            );
         }
 
         foreach ($collection->getInsertDiff() as $element) {
-            $this->conn->executeUpdate($insertSql, $this->getInsertRowSQLParameters($collection, $element));
+            $this->conn->executeUpdate(
+                $insertSql,
+                $this->getInsertRowSQLParameters($collection, $element),
+                $insertTypes
+            );
         }
     }
 
@@ -99,6 +108,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
     {
         $conditions     = array();
         $params         = array();
+        $types          = array();
         $mapping        = $collection->getMapping();
         $id             = $this->uow->getEntityIdentifier($collection->getOwner());
         $sourceClass    = $this->em->getClassMetadata($mapping['sourceEntity']);
@@ -117,6 +127,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
             $referencedName = $joinColumn['referencedColumnName'];
             $conditions[]   = 't.' . $columnName . ' = ?';
             $params[]       = $id[$sourceClass->getFieldForColumn($referencedName)];
+            $types[]        = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);
         }
 
         list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
@@ -147,7 +158,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
             . $joinTargetEntitySQL
             . ' WHERE ' . implode(' AND ', $conditions);
 
-        return $this->conn->fetchColumn($sql, $params);
+        return $this->conn->fetchColumn($sql, $params, 0, $types);
     }
 
     /**
@@ -171,11 +182,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
             throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
         }
 
-        list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
+        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
 
         $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
 
-        return (bool) $this->conn->fetchColumn($sql, $params);
+        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
     }
 
     /**
@@ -187,11 +198,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
             return false;
         }
 
-        list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($collection, $element, true);
+        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true);
 
         $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
 
-        return (bool) $this->conn->fetchColumn($sql, $params);
+        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
     }
 
     /**
@@ -203,11 +214,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
             return false;
         }
 
-        list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($collection, $element, false);
+        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false);
 
         $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
 
-        return (bool) $this->conn->executeUpdate($sql, $params);
+        return (bool) $this->conn->executeUpdate($sql, $params, $types);
     }
 
     /**
@@ -397,24 +408,32 @@ class ManyToManyPersister extends AbstractCollectionPersister
      *
      * @param \Doctrine\ORM\PersistentCollection $collection
      *
-     * @return string
+     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
+     *                             of types for bound parameters
      */
     protected function getDeleteRowSQL(PersistentCollection $collection)
     {
-        $mapping = $collection->getMapping();
-        $class   = $this->em->getClassMetadata($mapping['sourceEntity']);
-        $columns = array();
+        $mapping     = $collection->getMapping();
+        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
+        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
+        $columns     = array();
+        $types       = array();
 
         foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
             $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
+            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
         }
 
         foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
-            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
+            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
+            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
         }
 
-        return 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
-            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
+        return array(
+            'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
+            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',
+            $types,
+        );
     }
 
     /**
@@ -438,26 +457,34 @@ class ManyToManyPersister extends AbstractCollectionPersister
      *
      * @param \Doctrine\ORM\PersistentCollection $collection
      *
-     * @return string
+     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
+     *                             of types for bound parameters
      */
     protected function getInsertRowSQL(PersistentCollection $collection)
     {
-        $columns = array();
-        $mapping = $collection->getMapping();
-        $class   = $this->em->getClassMetadata($mapping['sourceEntity']);
+        $columns     = array();
+        $types       = array();
+        $mapping     = $collection->getMapping();
+        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
+        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
 
         foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
-            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
+            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
+            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
         }
 
         foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
-            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
+            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
+            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
         }
 
-        return 'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
+        return array(
+            'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
             . ' (' . implode(', ', $columns) . ')'
             . ' VALUES'
-            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
+            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',
+            $types,
+        );
     }
 
     /**
@@ -525,33 +552,40 @@ class ManyToManyPersister extends AbstractCollectionPersister
      * @param string                             $key
      * @param boolean                            $addFilters Whether the filter SQL should be included or not.
      *
-     * @return array
+     * @return array ordered vector:
+     *                - quoted join table name
+     *                - where clauses to be added for filtering
+     *                - parameters to be bound for filtering
+     *                - types of the parameters to be bound for filtering
      */
     private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
     {
-        $filterMapping  = $collection->getMapping();
-        $mapping        = $filterMapping;
-        $indexBy        = $mapping['indexBy'];
-        $id             = $this->uow->getEntityIdentifier($collection->getOwner());
-
-        $targetEntity   = $this->em->getClassMetadata($mapping['targetEntity']);
+        $filterMapping = $collection->getMapping();
+        $mapping       = $filterMapping;
+        $indexBy       = $mapping['indexBy'];
+        $id            = $this->uow->getEntityIdentifier($collection->getOwner());
+        $sourceClass   = $this->em->getClassMetadata($mapping['sourceEntity']);
+        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
 
         if (! $mapping['isOwningSide']) {
             $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
-            $mapping  = $associationSourceClass->associationMappings[$mapping['mappedBy']];
-            $joinColumns = $mapping['joinTable']['joinColumns'];
-            $relationMode = 'relationToTargetKeyColumns';
+            $mapping                = $associationSourceClass->associationMappings[$mapping['mappedBy']];
+            $joinColumns            = $mapping['joinTable']['joinColumns'];
+            $sourceRelationMode     = 'relationToTargetKeyColumns';
+            $targetRelationMode     = 'relationToSourceKeyColumns';
         } else {
-            $joinColumns = $mapping['joinTable']['inverseJoinColumns'];
             $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
-            $relationMode = 'relationToSourceKeyColumns';
+            $joinColumns            = $mapping['joinTable']['inverseJoinColumns'];
+            $sourceRelationMode     = 'relationToSourceKeyColumns';
+            $targetRelationMode     = 'relationToTargetKeyColumns';
         }
 
         $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t';
         $whereClauses    = array();
         $params          = array();
+        $types           = array();
 
-        $joinNeeded = !in_array($indexBy, $targetEntity->identifier);
+        $joinNeeded = ! in_array($indexBy, $targetClass->identifier);
 
         if ($joinNeeded) { // extra join needed if indexBy is not a @id
             $joinConditions = array();
@@ -559,21 +593,30 @@ class ManyToManyPersister extends AbstractCollectionPersister
             foreach ($joinColumns as $joinTableColumn) {
                 $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
             }
-            $tableName = $this->quoteStrategy->getTableName($targetEntity, $this->platform);
+
+            $tableName        = $this->quoteStrategy->getTableName($targetClass, $this->platform);
             $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
+            $columnName       = $targetClass->getColumnName($indexBy);
 
-            $whereClauses[] = 'tr.' . $targetEntity->getColumnName($indexBy) . ' = ?';
-            $params[] = $key;
-
+            $whereClauses[] = 'tr.' . $columnName . ' = ?';
+            $params[]       = $key;
+            $types[]        = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em);
         }
 
         foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
-            if (isset($mapping[$relationMode][$joinTableColumn])) {
+            if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
+                $column         = $mapping[$sourceRelationMode][$joinTableColumn];
                 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
-                $params[] = $id[$targetEntity->getFieldForColumn($mapping[$relationMode][$joinTableColumn])];
-            } elseif (!$joinNeeded) {
+                $params[]       = $sourceClass->containsForeignIdentifier
+                    ? $id[$sourceClass->getFieldForColumn($column)]
+                    : $id[$sourceClass->fieldNames[$column]];
+                $types[]        = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);
+            } elseif ( ! $joinNeeded) {
+                $column = $mapping[$targetRelationMode][$joinTableColumn];
+
                 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
-                $params[] = $key;
+                $params[]       = $key;
+                $types[]        = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em);
             }
         }
 
@@ -586,7 +629,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
             }
         }
 
-        return array($quotedJoinTable, $whereClauses, $params);
+        return array($quotedJoinTable, $whereClauses, $params, $types);
     }
 
     /**
@@ -594,7 +637,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
      * @param object                             $element
      * @param boolean                            $addFilters Whether the filter SQL should be included or not.
      *
-     * @return array
+     * @return array ordered vector:
+     *                - quoted join table name
+     *                - where clauses to be added for filtering
+     *                - parameters to be bound for filtering
+     *                - types of the parameters to be bound for filtering
      */
     private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
     {
@@ -618,18 +665,23 @@ class ManyToManyPersister extends AbstractCollectionPersister
         $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
         $whereClauses    = array();
         $params          = array();
+        $types           = array();
 
         foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
             $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
 
             if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
-                $params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
+                $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
+                $params[]     = $targetId[$targetClass->getFieldForColumn($targetColumn)];
+                $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
 
                 continue;
             }
 
             // relationToSourceKeyColumns
-            $params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
+            $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
+            $params[]     = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
+            $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);
         }
 
         if ($addFilters) {
@@ -643,7 +695,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
             }
         }
 
-        return array($quotedJoinTable, $whereClauses, $params);
+        return array($quotedJoinTable, $whereClauses, $params, $types);
     }
 
     /**
diff --git a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php
index ee550a922..9b14afde5 100644
--- a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php
@@ -67,7 +67,17 @@ class OneToManyPersister extends AbstractCollectionPersister
 
         $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
 
-        return $persister->load(array($mapping['mappedBy'] => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), null, 1);
+        return $persister->load(
+            array(
+                $mapping['mappedBy'] => $collection->getOwner(),
+                $mapping['indexBy']  => $index
+            ),
+            null,
+            $mapping,
+            array(),
+            null,
+            1
+        );
     }
 
     /**
diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
index 537961420..c53e817c1 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
@@ -36,6 +36,7 @@ use Doctrine\ORM\Persisters\SqlValueVisitor;
 use Doctrine\ORM\Query;
 use Doctrine\ORM\UnitOfWork;
 use Doctrine\ORM\Utility\IdentifierFlattener;
+use Doctrine\ORM\Utility\PersisterHelper;
 
 /**
  * A BasicEntityPersister maps an entity to a single table in a relational database.
@@ -610,15 +611,16 @@ class BasicEntityPersister implements EntityPersister
      */
     protected function prepareUpdateData($entity)
     {
-        $result = array();
-        $uow    = $this->em->getUnitOfWork();
+        $versionField = null;
+        $result       = array();
+        $uow          = $this->em->getUnitOfWork();
 
         if (($versioned = $this->class->isVersioned) != false) {
             $versionField = $this->class->versionField;
         }
 
         foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
-            if ($versioned && $versionField == $field) {
+            if (isset($versionField) && $versionField == $field) {
                 continue;
             }
 
@@ -668,7 +670,7 @@ class BasicEntityPersister implements EntityPersister
                 $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
 
                 $this->quotedColumns[$sourceColumn]  = $quotedColumn;
-                $this->columnTypes[$sourceColumn]    = $targetClass->getTypeOfColumn($targetColumn);
+                $this->columnTypes[$sourceColumn]    = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
                 $result[$owningTable][$sourceColumn] = $newValId
                     ? $newValId[$targetClass->getFieldForColumn($targetColumn)]
                     : null;
@@ -862,12 +864,12 @@ class BasicEntityPersister implements EntityPersister
         list($params, $types) = $valueVisitor->getParamsAndTypes();
 
         foreach ($params as $param) {
-            $sqlParams[] = $this->getValue($param);
+            $sqlParams[] = PersisterHelper::getIdentifierValues($param, $this->em);
         }
 
         foreach ($types as $type) {
             list($field, $value) = $type;
-            $sqlTypes[]          = $this->getType($field, $value);
+            $sqlTypes[]          = $this->getType($field, $value, $this->class);
         }
 
         return array($sqlParams, $sqlTypes);
@@ -969,7 +971,7 @@ class BasicEntityPersister implements EntityPersister
         $class          = $sourceClass;
         $association    = $assoc;
         $criteria       = array();
-
+        $parameters     = array();
 
         if ( ! $assoc['isOwningSide']) {
             $class       = $this->em->getClassMetadata($assoc['targetEntity']);
@@ -1012,10 +1014,15 @@ class BasicEntityPersister implements EntityPersister
             }
 
             $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value;
+            $parameters[] = array(
+                'value' => $value,
+                'field' => $field,
+                'class' => $sourceClass,
+            );
         }
 
         $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);
-        list($params, $types) = $this->expandParameters($criteria);
+        list($params, $types) = $this->expandToManyParameters($parameters);
 
         return $this->conn->executeQuery($sql, $params, $types);
     }
@@ -1309,10 +1316,7 @@ class BasicEntityPersister implements EntityPersister
             $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
             $columnList[]     = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
                                 . '.' . $quotedColumn . ' AS ' . $resultColumnName;
-
-            if (isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
-                $type  = $targetClass->fieldMappings[$targetClass->fieldNames[$joinColumn['referencedColumnName']]]['type'];
-            }
+            $type             = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
 
             $this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
         }
@@ -1426,7 +1430,7 @@ class BasicEntityPersister implements EntityPersister
             }
 
             if ($this->class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->class->identifier[0] != $name) {
-                $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform);
+                $columns[]                = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform);
                 $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type'];
             }
         }
@@ -1713,7 +1717,8 @@ class BasicEntityPersister implements EntityPersister
      */
     private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
     {
-        $criteria = array();
+        $criteria    = array();
+        $parameters  = array();
         $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']];
         $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']);
 
@@ -1730,15 +1735,29 @@ class BasicEntityPersister implements EntityPersister
                 }
 
                 $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
+                $parameters[]                                   = array(
+                    'value' => $value,
+                    'field' => $field,
+                    'class' => $sourceClass,
+                );
 
                 continue;
             }
 
-            $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
+            $field = $sourceClass->fieldNames[$sourceKeyColumn];
+            $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
+
+            $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
+            $parameters[] = array(
+                'value' => $value,
+                'field' => $field,
+                'class' => $sourceClass,
+            );
+
         }
 
-        $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);
-        list($params, $types) = $this->expandParameters($criteria);
+        $sql                  = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);
+        list($params, $types) = $this->expandToManyParameters($parameters);
 
         return $this->conn->executeQuery($sql, $params, $types);
     }
@@ -1756,72 +1775,56 @@ class BasicEntityPersister implements EntityPersister
                 continue; // skip null values.
             }
 
-            $types[]  = $this->getType($field, $value);
+            $types[]  = $this->getType($field, $value, $this->class);
             $params[] = $this->getValue($value);
         }
 
         return array($params, $types);
     }
 
+    /**
+     * Expands the parameters from the given criteria and use the correct binding types if found,
+     * specialized for OneToMany or ManyToMany associations.
+     *
+     * @param mixed[][] $criteria an array of arrays containing following:
+     *                             - field to which each criterion will be bound
+     *                             - value to be bound
+     *                             - class to which the field belongs to
+     *
+     *
+     * @return array
+     */
+    private function expandToManyParameters($criteria)
+    {
+        $params = array();
+        $types  = array();
+
+        foreach ($criteria as $criterion) {
+            if ($criterion['value'] === null) {
+                continue; // skip null values.
+            }
+
+            $types[]  = $this->getType($criterion['field'], $criterion['value'], $criterion['class']);
+            $params[] = PersisterHelper::getIdentifierValues($criterion['value'], $this->em);
+        }
+
+        return array($params, $types);
+    }
+
     /**
      * Infers field type to be used by parameter type casting.
      *
-     * @param string $field
-     * @param mixed  $value
+     * @param string        $fieldName
+     * @param mixed         $value
+     * @param ClassMetadata $class
      *
      * @return integer
      *
      * @throws \Doctrine\ORM\Query\QueryException
      */
-    private function getType($field, $value)
+    private function getType($fieldName, $value, ClassMetadata $class)
     {
-        switch (true) {
-            case (isset($this->class->fieldMappings[$field])):
-                $type = $this->class->fieldMappings[$field]['type'];
-                break;
-
-            case (isset($this->class->associationMappings[$field]) && $this->class->associationMappings[$field]['type'] === ClassMetadata::MANY_TO_MANY):
-                $assoc       = $this->class->associationMappings[$field];
-                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
-
-                if ( ! $assoc['isOwningSide']) {
-                    $assoc = $targetClass->associationMappings[$assoc['mappedBy']];
-                }
-
-                if (count($assoc['relationToTargetKeyColumns']) > 1) {
-                    throw Query\QueryException::associationPathCompositeKeyNotSupported();
-                }
-
-                $targetClass  = $this->em->getClassMetadata($assoc['targetEntity']);
-                $targetColumn = $assoc['joinTable']['inverseJoinColumns'][0]['referencedColumnName'];
-                $type         = null;
-
-                if (isset($targetClass->fieldNames[$targetColumn])) {
-                    $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type'];
-                }
-
-                break;
-
-            case (isset($this->class->associationMappings[$field])):
-                $assoc = $this->class->associationMappings[$field];
-
-                if (count($assoc['sourceToTargetKeyColumns']) > 1) {
-                    throw Query\QueryException::associationPathCompositeKeyNotSupported();
-                }
-
-                $targetClass  = $this->em->getClassMetadata($assoc['targetEntity']);
-                $targetColumn = $assoc['joinColumns'][0]['referencedColumnName'];
-                $type         = null;
-
-                if (isset($targetClass->fieldNames[$targetColumn])) {
-                    $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type'];
-                }
-
-                break;
-
-            default:
-                $type = null;
-        }
+        $type = PersisterHelper::getTypeOfField($fieldName, $class, $this->em);
 
         if (is_array($value)) {
             $type = Type::getType($type)->getBindingType();
@@ -1886,20 +1889,21 @@ class BasicEntityPersister implements EntityPersister
              . $this->getLockTablesSql(null)
              . ' WHERE ' . $this->getSelectConditionSQL($criteria);
 
-        list($params) = $this->expandParameters($criteria);
+        list($params, $types) = $this->expandParameters($criteria);
 
         if (null !== $extraConditions) {
-            $sql                           .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions);
-            list($criteriaParams, $values) = $this->expandCriteriaParameters($extraConditions);
+            $sql                                 .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions);
+            list($criteriaParams, $criteriaTypes) = $this->expandCriteriaParameters($extraConditions);
 
             $params = array_merge($params, $criteriaParams);
+            $types  = array_merge($types, $criteriaTypes);
         }
 
         if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) {
             $sql .= ' AND ' . $filterSql;
         }
 
-        return (bool) $this->conn->fetchColumn($sql, $params);
+        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
     }
 
     /**
diff --git a/lib/Doctrine/ORM/Utility/PersisterHelper.php b/lib/Doctrine/ORM/Utility/PersisterHelper.php
new file mode 100644
index 000000000..6c3af11da
--- /dev/null
+++ b/lib/Doctrine/ORM/Utility/PersisterHelper.php
@@ -0,0 +1,172 @@
+<?php
+
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\ORM\Utility;
+
+use Doctrine\Common\Util\ClassUtils;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Doctrine\ORM\Query\QueryException;
+
+/**
+ * The PersisterHelper contains logic to infer binding types which is used in
+ * several persisters.
+ *
+ * @link   www.doctrine-project.org
+ * @since  2.5
+ * @author Jasper N. Brouwer <jasper@nerdsweide.nl>
+ */
+class PersisterHelper
+{
+    /**
+     * @param string                 $fieldName
+     * @param ClassMetadata          $class
+     * @param EntityManagerInterface $em
+     *
+     * @return string|null
+     *
+     * @throws QueryException
+     */
+    public static function getTypeOfField($fieldName, ClassMetadata $class, EntityManagerInterface $em)
+    {
+        if (isset($class->fieldMappings[$fieldName])) {
+            return $class->fieldMappings[$fieldName]['type'];
+        }
+
+        if ( ! isset($class->associationMappings[$fieldName])) {
+            return null;
+        }
+
+        $assoc = $class->associationMappings[$fieldName];
+
+        if (! $assoc['isOwningSide']) {
+            return self::getTypeOfField($assoc['mappedBy'], $em->getClassMetadata($assoc['targetEntity']), $em);
+        }
+
+        if (($assoc['type'] & ClassMetadata::MANY_TO_MANY) > 0) {
+            $joinData = $assoc['joinTable'];
+        } else {
+            $joinData = $assoc;
+        }
+
+        if (count($joinData['joinColumns']) > 1) {
+            throw QueryException::associationPathCompositeKeyNotSupported();
+        }
+
+        $targetColumnName = $joinData['joinColumns'][0]['referencedColumnName'];
+        $targetClass      = $em->getClassMetadata($assoc['targetEntity']);
+
+        return self::getTypeOfColumn($targetColumnName, $targetClass, $em);
+    }
+
+    /**
+     * @param string                 $columnName
+     * @param ClassMetadata          $class
+     * @param EntityManagerInterface $em
+     *
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    public static function getTypeOfColumn($columnName, ClassMetadata $class, EntityManagerInterface $em)
+    {
+        if (isset($class->fieldNames[$columnName])) {
+            $fieldName = $class->fieldNames[$columnName];
+
+            if (isset($class->fieldMappings[$fieldName])) {
+                return $class->fieldMappings[$fieldName]['type'];
+            }
+        }
+
+        // iterate over to-one association mappings
+        foreach ($class->associationMappings as $assoc) {
+            if ( ! isset($assoc['joinColumns'])) {
+                continue;
+            }
+
+            foreach ($assoc['joinColumns'] as $joinColumn) {
+                if ($joinColumn['name'] == $columnName) {
+                    $targetColumnName = $joinColumn['referencedColumnName'];
+                    $targetClass      = $em->getClassMetadata($assoc['targetEntity']);
+
+                    return self::getTypeOfColumn($targetColumnName, $targetClass, $em);
+                }
+            }
+        }
+
+        // iterate over to-many association mappings
+        foreach ($class->associationMappings as $assoc) {
+            if ( ! (isset($assoc['joinTable']) && isset($assoc['joinTable']['joinColumns']))) {
+                continue;
+            }
+
+            foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) {
+                if ($joinColumn['name'] == $columnName) {
+                    $targetColumnName = $joinColumn['referencedColumnName'];
+                    $targetClass      = $em->getClassMetadata($assoc['targetEntity']);
+
+                    return self::getTypeOfColumn($targetColumnName, $targetClass, $em);
+                }
+            }
+        }
+
+        throw new \RuntimeException(sprintf(
+            'Could not resolve type of column "%s" of class "%s"',
+            $columnName,
+            $class->getName()
+        ));
+    }
+
+    /**
+     * @param mixed                  $value
+     * @param EntityManagerInterface $em
+     *
+     * @return mixed
+     */
+    public static function getIdentifierValues($value, EntityManagerInterface $em)
+    {
+        if ( ! is_array($value)) {
+            return self::getIndividualValue($value, $em);
+        }
+
+        $newValue = array();
+
+        foreach ($value as $fieldName => $fieldValue) {
+            $newValue[$fieldName] = self::getIndividualValue($fieldValue, $em);
+        }
+
+        return $newValue;
+    }
+
+    /**
+     * @param mixed                  $value
+     * @param EntityManagerInterface $em
+     *
+     * @return mixed
+     */
+    private static function getIndividualValue($value, EntityManagerInterface $em)
+    {
+        if ( ! is_object($value) || ! $em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
+            return $value;
+        }
+
+        return $em->getUnitOfWork()->getSingleIdentifierValue($value);
+    }
+}
diff --git a/tests/Doctrine/Tests/DbalTypes/Rot13Type.php b/tests/Doctrine/Tests/DbalTypes/Rot13Type.php
new file mode 100644
index 000000000..6403b9ebe
--- /dev/null
+++ b/tests/Doctrine/Tests/DbalTypes/Rot13Type.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Doctrine\Tests\DbalTypes;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\Type;
+
+/**
+ * Shifts every letter by 13 places in the alphabet (ROT13 encoding).
+ */
+class Rot13Type extends Type
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null      $value
+     * @param AbstractPlatform $platform
+     *
+     * @return string|null
+     */
+    public function convertToDatabaseValue($value, AbstractPlatform $platform)
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        return str_rot13($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null      $value
+     * @param AbstractPlatform $platform
+     *
+     * @return string|null
+     */
+    public function convertToPHPValue($value, AbstractPlatform $platform)
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        return str_rot13($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param array            $fieldDeclaration
+     * @param AbstractPlatform $platform
+     *
+     * @return string
+     */
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
+    {
+        return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param AbstractPlatform $platform
+     *
+     * @return int|null
+     */
+    public function getDefaultLength(AbstractPlatform $platform)
+    {
+        return $platform->getVarcharDefaultLength();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return 'rot13';
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/AuxiliaryEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/AuxiliaryEntity.php
new file mode 100644
index 000000000..89025605b
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/AuxiliaryEntity.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_auxiliary")
+ */
+class AuxiliaryEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id4;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php
new file mode 100644
index 000000000..8d0a56d01
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_manytomany_compositeid")
+ */
+class InversedManyToManyCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToMany(targetEntity="OwningManyToManyCompositeIdEntity", mappedBy="associatedEntities")
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..9cc1a1207
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_manytomany_compositeid_foreignkey")
+ */
+class InversedManyToManyCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @ManyToOne(targetEntity="AuxiliaryEntity")
+     * @JoinColumn(name="foreign_id", referencedColumnName="id4")
+     * @Id
+     */
+    public $foreignEntity;
+
+    /**
+     * @ManyToMany(targetEntity="OwningManyToManyCompositeIdForeignKeyEntity", mappedBy="associatedEntities")
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyEntity.php
new file mode 100644
index 000000000..3b0157655
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyEntity.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_manytomany")
+ */
+class InversedManyToManyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @ManyToMany(targetEntity="OwningManyToManyEntity", mappedBy="associatedEntities")
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php
new file mode 100644
index 000000000..b139fa407
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_manytomany_extralazy")
+ */
+class InversedManyToManyExtraLazyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @ManyToMany(
+     *     targetEntity="OwningManyToManyExtraLazyEntity",
+     *     mappedBy="associatedEntities",
+     *     fetch="EXTRA_LAZY",
+     *     indexBy="id2"
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php
new file mode 100644
index 000000000..01892c1aa
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetomany_compositeid")
+ */
+class InversedOneToManyCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    /**
+     * @OneToMany(targetEntity="OwningManyToOneCompositeIdEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..e2212812e
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetomany_compositeid_foreignkey")
+ */
+class InversedOneToManyCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @ManyToOne(targetEntity="AuxiliaryEntity")
+     * @JoinColumn(name="foreign_id", referencedColumnName="id4")
+     * @Id
+     */
+    public $foreignEntity;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    /**
+     * @OneToMany(targetEntity="OwningManyToOneCompositeIdForeignKeyEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyEntity.php
new file mode 100644
index 000000000..ff8ec73a0
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyEntity.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetomany")
+ */
+class InversedOneToManyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @OneToMany(targetEntity="OwningManyToOneEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntities;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php
new file mode 100644
index 000000000..26d0a58d0
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetomany_extralazy")
+ */
+class InversedOneToManyExtraLazyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @OneToMany(
+     *     targetEntity="OwningManyToOneExtraLazyEntity",
+     *     mappedBy="associatedEntity",
+     *     fetch="EXTRA_LAZY",
+     *     indexBy="id2"
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php
new file mode 100644
index 000000000..631b22b8b
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetoone_compositeid")
+ */
+class InversedOneToOneCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    /**
+     * @OneToOne(targetEntity="OwningOneToOneCompositeIdEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..fb6d43026
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetoone_compositeid_foreignkey")
+ */
+class InversedOneToOneCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @ManyToOne(targetEntity="AuxiliaryEntity")
+     * @JoinColumn(name="foreign_id", referencedColumnName="id4")
+     * @Id
+     */
+    public $foreignEntity;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    /**
+     * @OneToOne(targetEntity="OwningOneToOneCompositeIdForeignKeyEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneEntity.php
new file mode 100644
index 000000000..d652908e2
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/InversedOneToOneEntity.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_inversed_onetoone")
+ */
+class InversedOneToOneEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id1;
+
+    /**
+     * @Column(type="string", name="some_property")
+     */
+    public $someProperty;
+
+    /**
+     * @OneToOne(targetEntity="OwningOneToOneEntity", mappedBy="associatedEntity")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php
new file mode 100644
index 000000000..01c08776f
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytomany_compositeid")
+ */
+class OwningManyToManyCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id3;
+
+    /**
+     * @ManyToMany(targetEntity="InversedManyToManyCompositeIdEntity", inversedBy="associatedEntities")
+     * @JoinTable(
+     *     name="vct_xref_manytomany_compositeid",
+     *     joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id3")},
+     *     inverseJoinColumns={
+     *         @JoinColumn(name="inversed_id1", referencedColumnName="id1"),
+     *         @JoinColumn(name="inversed_id2", referencedColumnName="id2")
+     *     }
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..abbb32d8a
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytomany_compositeid_foreignkey")
+ */
+class OwningManyToManyCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToMany(targetEntity="InversedManyToManyCompositeIdForeignKeyEntity", inversedBy="associatedEntities")
+     * @JoinTable(
+     *     name="vct_xref_manytomany_compositeid_foreignkey",
+     *     joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")},
+     *     inverseJoinColumns={
+     *         @JoinColumn(name="associated_id", referencedColumnName="id1"),
+     *         @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id")
+     *     }
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyEntity.php
new file mode 100644
index 000000000..292ff7835
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyEntity.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytomany")
+ */
+class OwningManyToManyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToMany(targetEntity="InversedManyToManyEntity", inversedBy="associatedEntities")
+     * @JoinTable(
+     *     name="vct_xref_manytomany",
+     *     joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")},
+     *     inverseJoinColumns={@JoinColumn(name="inversed_id", referencedColumnName="id1")}
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php
new file mode 100644
index 000000000..dda25b27d
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytomany_extralazy")
+ */
+class OwningManyToManyExtraLazyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToMany(
+     *     targetEntity="InversedManyToManyExtraLazyEntity",
+     *     inversedBy="associatedEntities",
+     *     fetch="EXTRA_LAZY",
+     *     indexBy="id1"
+     * )
+     * @JoinTable(
+     *     name="vct_xref_manytomany_extralazy",
+     *     joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")},
+     *     inverseJoinColumns={@JoinColumn(name="inversed_id", referencedColumnName="id1")}
+     * )
+     */
+    public $associatedEntities;
+
+    public function __construct()
+    {
+        $this->associatedEntities = new ArrayCollection();
+    }
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php
new file mode 100644
index 000000000..b06b2188b
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytoone_compositeid")
+ */
+class OwningManyToOneCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id3;
+
+    /**
+     * @ManyToOne(targetEntity="InversedOneToManyCompositeIdEntity", inversedBy="associatedEntities")
+     * @JoinColumns({
+     *     @JoinColumn(name="associated_id1", referencedColumnName="id1"),
+     *     @JoinColumn(name="associated_id2", referencedColumnName="id2")
+     * })
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..b6787a418
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytoone_compositeid_foreignkey")
+ */
+class OwningManyToOneCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToOne(targetEntity="InversedOneToManyCompositeIdForeignKeyEntity", inversedBy="associatedEntities")
+     * @JoinColumns({
+     *     @JoinColumn(name="associated_id", referencedColumnName="id1"),
+     *     @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id")
+     * })
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneEntity.php
new file mode 100644
index 000000000..b05a86b13
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneEntity.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytoone")
+ */
+class OwningManyToOneEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToOne(targetEntity="InversedOneToManyEntity", inversedBy="associatedEntities")
+     * @JoinColumn(name="associated_id", referencedColumnName="id1")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php
new file mode 100644
index 000000000..aa4df0545
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_manytoone_extralazy")
+ */
+class OwningManyToOneExtraLazyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @ManyToOne(targetEntity="InversedOneToManyExtraLazyEntity", inversedBy="associatedEntities")
+     * @JoinColumn(name="associated_id", referencedColumnName="id1")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php
new file mode 100644
index 000000000..bf4282cc1
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_onetoone_compositeid")
+ */
+class OwningOneToOneCompositeIdEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id3;
+
+    /**
+     * @OneToOne(targetEntity="InversedOneToOneCompositeIdEntity", inversedBy="associatedEntity")
+     * @JoinColumns({
+     *     @JoinColumn(name="associated_id1", referencedColumnName="id1"),
+     *     @JoinColumn(name="associated_id2", referencedColumnName="id2")
+     * })
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php
new file mode 100644
index 000000000..708905b1a
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(
+ *     name="vct_owning_onetoone_compositeid_foreignkey",
+ *     uniqueConstraints={
+ *         @UniqueConstraint(name="associated_entity_uniq", columns={"associated_id", "associated_foreign_id"})
+ *     }
+ * )
+ */
+class OwningOneToOneCompositeIdForeignKeyEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @OneToOne(targetEntity="InversedOneToOneCompositeIdForeignKeyEntity", inversedBy="associatedEntity")
+     * @JoinColumns({
+     *     @JoinColumn(name="associated_id", referencedColumnName="id1"),
+     *     @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id")
+     * })
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneEntity.php b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneEntity.php
new file mode 100644
index 000000000..338cd5ba7
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/ValueConversionType/OwningOneToOneEntity.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Doctrine\Tests\Models\ValueConversionType;
+
+/**
+ * @Entity
+ * @Table(name="vct_owning_onetoone")
+ */
+class OwningOneToOneEntity
+{
+    /**
+     * @Column(type="rot13")
+     * @Id
+     */
+    public $id2;
+
+    /**
+     * @OneToOne(targetEntity="InversedOneToOneEntity", inversedBy="associatedEntity")
+     * @JoinColumn(name="associated_id", referencedColumnName="id1")
+     */
+    public $associatedEntity;
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaValidatorTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaValidatorTest.php
index c09f336c9..948aad2ef 100644
--- a/tests/Doctrine/Tests/ORM/Functional/SchemaValidatorTest.php
+++ b/tests/Doctrine/Tests/ORM/Functional/SchemaValidatorTest.php
@@ -2,6 +2,7 @@
 
 namespace Doctrine\Tests\ORM\Functional;
 
+use Doctrine\DBAL\Types\Type as DBALType;
 use Doctrine\ORM\Tools\SchemaValidator;
 
 /**
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php
new file mode 100644
index 000000000..877d1a279
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that ManyToMany associations with composite id of which one is a
+ * association itself work correctly.
+ *
+ * @group DDC-3380
+ */
+class ManyToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_manytomany_compositeid_foreignkey');
+
+        parent::setUp();
+
+        $auxiliary = new Entity\AuxiliaryEntity();
+        $auxiliary->id4 = 'abc';
+
+        $inversed = new Entity\InversedManyToManyCompositeIdForeignKeyEntity();
+        $inversed->id1 = 'def';
+        $inversed->foreignEntity = $auxiliary;
+
+        $owning = new Entity\OwningManyToManyCompositeIdForeignKeyEntity();
+        $owning->id2 = 'ghi';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntities->add($inversed);
+
+        $this->_em->persist($auxiliary);
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_xref_manytomany_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_owning_manytomany_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_inversed_manytomany_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_auxiliary');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id4 FROM vct_auxiliary LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id1 FROM vct_inversed_manytomany_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT foreign_id FROM vct_inversed_manytomany_compositeid_foreignkey LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id2 FROM vct_owning_manytomany_compositeid_foreignkey LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT associated_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_foreign_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT owning_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity', $auxiliary);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $auxiliary->id4);
+        $this->assertEquals('def', $inversed->id1);
+        $this->assertEquals('abc', $inversed->foreignEntity->id4);
+        $this->assertEquals('ghi', $owning->id2);
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase
+     */
+    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => $auxiliary)
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity', $inversed);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertCount(1, $owning->associatedEntities);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+
+    /**
+     * @depends testThatTheCollectionFromOwningToInversedIsLoaded
+     * @depends testThatTheCollectionFromInversedToOwningIsLoaded
+     */
+    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation()
+    {
+        $conn = $this->_em->getConnection();
+
+        // remove association
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        foreach ($inversed->associatedEntities as $owning) {
+            $inversed->associatedEntities->removeElement($owning);
+            $owning->associatedEntities->removeElement($inversed);
+        }
+
+        $this->_em->flush();
+        $this->_em->clear();
+
+        // test association is removed
+
+        $this->assertEquals(0, $conn->fetchColumn('SELECT COUNT(*) FROM vct_xref_manytomany_compositeid_foreignkey'));
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php
new file mode 100644
index 000000000..6b56073a3
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that ManyToMany associations with composite id work correctly.
+ *
+ * @group DDC-3380
+ */
+class ManyToManyCompositeIdTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_manytomany_compositeid');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedManyToManyCompositeIdEntity();
+        $inversed->id1 = 'abc';
+        $inversed->id2 = 'def';
+
+        $owning = new Entity\OwningManyToManyCompositeIdEntity();
+        $owning->id3 = 'ghi';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntities->add($inversed);
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_xref_manytomany_compositeid');
+        $conn->executeUpdate('DROP TABLE vct_owning_manytomany_compositeid');
+        $conn->executeUpdate('DROP TABLE vct_inversed_manytomany_compositeid');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_manytomany_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_inversed_manytomany_compositeid LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id3 FROM vct_owning_manytomany_compositeid LIMIT 1'));
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT inversed_id1 FROM vct_xref_manytomany_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT inversed_id2 FROM vct_xref_manytomany_compositeid LIMIT 1'));
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT owning_id FROM vct_xref_manytomany_compositeid LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $inversed->id2);
+        $this->assertEquals('ghi', $owning->id3);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertCount(1, $owning->associatedEntities);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+
+    /**
+     * @depends testThatTheCollectionFromOwningToInversedIsLoaded
+     * @depends testThatTheCollectionFromInversedToOwningIsLoaded
+     */
+    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation()
+    {
+        $conn = $this->_em->getConnection();
+
+        // remove association
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        foreach ($inversed->associatedEntities as $owning) {
+            $inversed->associatedEntities->removeElement($owning);
+            $owning->associatedEntities->removeElement($inversed);
+        }
+
+        $this->_em->flush();
+        $this->_em->clear();
+
+        // test association is removed
+
+        $this->assertEquals(0, $conn->fetchColumn('SELECT COUNT(*) FROM vct_xref_manytomany_compositeid'));
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php
new file mode 100644
index 000000000..3d1df24b8
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that ManyToMany associations work correctly, focusing on EXTRA_LAZY
+ * functionality.
+ *
+ * @group DDC-3380
+ */
+class ManyToManyExtraLazyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_manytomany_extralazy');
+        parent::setUp();
+
+        $inversed1 = new Entity\InversedManyToManyExtraLazyEntity();
+        $inversed1->id1 = 'abc';
+
+        $inversed2 = new Entity\InversedManyToManyExtraLazyEntity();
+        $inversed2->id1 = 'def';
+
+        $owning1 = new Entity\OwningManyToManyExtraLazyEntity();
+        $owning1->id2 = 'ghi';
+
+        $owning2 = new Entity\OwningManyToManyExtraLazyEntity();
+        $owning2->id2 = 'jkl';
+
+        $inversed1->associatedEntities->add($owning1);
+        $owning1->associatedEntities->add($inversed1);
+        $inversed1->associatedEntities->add($owning2);
+        $owning2->associatedEntities->add($inversed1);
+
+        $inversed2->associatedEntities->add($owning1);
+        $owning1->associatedEntities->add($inversed2);
+        $inversed2->associatedEntities->add($owning2);
+        $owning2->associatedEntities->add($inversed2);
+
+        $this->_em->persist($inversed1);
+        $this->_em->persist($inversed2);
+        $this->_em->persist($owning1);
+        $this->_em->persist($owning2);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_xref_manytomany_extralazy');
+        $conn->executeUpdate('DROP TABLE vct_owning_manytomany_extralazy');
+        $conn->executeUpdate('DROP TABLE vct_inversed_manytomany_extralazy');
+    }
+
+    public function testThatTheExtraLazyCollectionFromOwningToInversedIsCounted()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity',
+            'ghi'
+        );
+
+        $this->assertEquals(2, $owning->associatedEntities->count());
+    }
+
+    public function testThatTheExtraLazyCollectionFromInversedToOwningIsCounted()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertEquals(2, $inversed->associatedEntities->count());
+    }
+
+    public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnEntity()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity',
+            'ghi'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertTrue($owning->associatedEntities->contains($inversed));
+    }
+
+    public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnEntity()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity',
+            'ghi'
+        );
+
+        $this->assertTrue($inversed->associatedEntities->contains($owning));
+    }
+
+    public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnIndexByKey()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity',
+            'ghi'
+        );
+
+        $this->assertTrue($owning->associatedEntities->containsKey('abc'));
+    }
+
+    public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnIndexByKey()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertTrue($inversed->associatedEntities->containsKey('ghi'));
+    }
+
+    public function testThatASliceOfTheExtraLazyCollectionFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity',
+            'ghi'
+        );
+
+        $this->assertCount(1, $owning->associatedEntities->slice(0, 1));
+    }
+
+    public function testThatASliceOfTheExtraLazyCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities->slice(1, 1));
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php
new file mode 100644
index 000000000..66193fb14
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that ManyToMany associations work correctly.
+ *
+ * @group DDC-3380
+ */
+class ManyToManyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_manytomany');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedManyToManyEntity();
+        $inversed->id1 = 'abc';
+
+        $owning = new Entity\OwningManyToManyEntity();
+        $owning->id2 = 'def';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntities->add($inversed);
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_xref_manytomany');
+        $conn->executeUpdate('DROP TABLE vct_owning_manytomany');
+        $conn->executeUpdate('DROP TABLE vct_inversed_manytomany');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_manytomany LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_owning_manytomany LIMIT 1'));
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT inversed_id FROM vct_xref_manytomany LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT owning_id FROM vct_xref_manytomany LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity',
+            'def'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity',
+            'def'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $owning->id2);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity',
+            'def'
+        );
+
+        $this->assertCount(1, $owning->associatedEntities);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity',
+            'abc'
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+
+    /**
+     * @depends testThatTheCollectionFromOwningToInversedIsLoaded
+     * @depends testThatTheCollectionFromInversedToOwningIsLoaded
+     */
+    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation()
+    {
+        $conn = $this->_em->getConnection();
+
+        // remove association
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity',
+            'abc'
+        );
+
+        foreach ($inversed->associatedEntities as $owning) {
+            $inversed->associatedEntities->removeElement($owning);
+            $owning->associatedEntities->removeElement($inversed);
+        }
+
+        $this->_em->flush();
+        $this->_em->clear();
+
+        // test association is removed
+
+        $this->assertEquals(0, $conn->fetchColumn('SELECT COUNT(*) FROM vct_xref_manytomany'));
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php
new file mode 100644
index 000000000..17a8ea873
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToMany associations with composite id of which one is a
+ * association itself work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetomany_compositeid_foreignkey');
+
+        parent::setUp();
+
+        $auxiliary = new Entity\AuxiliaryEntity();
+        $auxiliary->id4 = 'abc';
+
+        $inversed = new Entity\InversedOneToManyCompositeIdForeignKeyEntity();
+        $inversed->id1 = 'def';
+        $inversed->foreignEntity = $auxiliary;
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningManyToOneCompositeIdForeignKeyEntity();
+        $owning->id2 = 'ghi';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($auxiliary);
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_manytoone_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetomany_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_auxiliary');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id4 FROM vct_auxiliary LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetomany_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT foreign_id FROM vct_inversed_onetomany_compositeid_foreignkey LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id2 FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT associated_id FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_foreign_id FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity', $auxiliary);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $auxiliary->id4);
+        $this->assertEquals('def', $inversed->id1);
+        $this->assertEquals('abc', $inversed->foreignEntity->id4);
+        $this->assertEquals('ghi', $owning->id2);
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase
+     */
+    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => $auxiliary)
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity', $inversed);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertSame('def', $inversedProxy->id1, 'Proxy identifier is converted');
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php
new file mode 100644
index 000000000..c094afa78
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToMany associations with composite id work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToManyCompositeIdTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetomany_compositeid');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedOneToManyCompositeIdEntity();
+        $inversed->id1 = 'abc';
+        $inversed->id2 = 'def';
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningManyToOneCompositeIdEntity();
+        $owning->id3 = 'ghi';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_manytoone_compositeid');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetomany_compositeid');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetomany_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_inversed_onetomany_compositeid LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id3 FROM vct_owning_manytoone_compositeid LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_id1 FROM vct_owning_manytoone_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT associated_id2 FROM vct_owning_manytoone_compositeid LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $inversed->id2);
+        $this->assertEquals('ghi', $owning->id3);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php
new file mode 100644
index 000000000..bb9006782
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToMany associations work correctly, focusing on EXTRA_LAZY
+ * functionality.
+ *
+ * @group DDC-3380
+ */
+class OneToManyExtraLazyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetomany_extralazy');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedOneToManyExtraLazyEntity();
+        $inversed->id1 = 'abc';
+
+        $owning1 = new Entity\OwningManyToOneExtraLazyEntity();
+        $owning1->id2 = 'def';
+
+        $owning2 = new Entity\OwningManyToOneExtraLazyEntity();
+        $owning2->id2 = 'ghi';
+
+        $owning3 = new Entity\OwningManyToOneExtraLazyEntity();
+        $owning3->id2 = 'jkl';
+
+        $inversed->associatedEntities->add($owning1);
+        $owning1->associatedEntity = $inversed;
+        $inversed->associatedEntities->add($owning2);
+        $owning2->associatedEntity = $inversed;
+        $inversed->associatedEntities->add($owning3);
+        $owning3->associatedEntity = $inversed;
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning1);
+        $this->_em->persist($owning2);
+        $this->_em->persist($owning3);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_manytoone_extralazy');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetomany_extralazy');
+    }
+
+    public function testThatExtraLazyCollectionIsCounted()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertEquals(3, $inversed->associatedEntities->count());
+    }
+
+    public function testThatExtraLazyCollectionContainsAnEntity()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneExtraLazyEntity',
+            'def'
+        );
+
+        $this->assertTrue($inversed->associatedEntities->contains($owning));
+    }
+
+    public function testThatExtraLazyCollectionContainsAnIndexbyKey()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertTrue($inversed->associatedEntities->containsKey('def'));
+    }
+
+    public function testThatASliceOfTheExtraLazyCollectionIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity',
+            'abc'
+        );
+
+        $this->assertCount(2, $inversed->associatedEntities->slice(0, 2));
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php
new file mode 100644
index 000000000..e83453714
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToMany associations work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToManyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetomany');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedOneToManyEntity();
+        $inversed->id1 = 'abc';
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningManyToOneEntity();
+        $owning->id2 = 'def';
+
+        $inversed->associatedEntities->add($owning);
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_manytoone');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetomany');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetomany LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_owning_manytoone LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_id FROM vct_owning_manytoone LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity',
+            'def'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity',
+            'def'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $owning->id2);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity',
+            'def'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheCollectionFromInversedToOwningIsLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity',
+            'abc'
+        );
+
+        $this->assertCount(1, $inversed->associatedEntities);
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php
new file mode 100644
index 000000000..4a595c21f
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToOne associations with composite id of which one is a
+ * association itself work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToOneCompositeIdForeignKeyTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetoone_compositeid_foreignkey');
+        parent::setUp();
+
+        $auxiliary = new Entity\AuxiliaryEntity();
+        $auxiliary->id4 = 'abc';
+
+        $inversed = new Entity\InversedOneToOneCompositeIdForeignKeyEntity();
+        $inversed->id1 = 'def';
+        $inversed->foreignEntity = $auxiliary;
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningOneToOneCompositeIdForeignKeyEntity();
+        $owning->id2 = 'ghi';
+
+        $inversed->associatedEntity = $owning;
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($auxiliary);
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_onetoone_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetoone_compositeid_foreignkey');
+        $conn->executeUpdate('DROP TABLE vct_auxiliary');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id4 FROM vct_auxiliary LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetoone_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT foreign_id FROM vct_inversed_onetoone_compositeid_foreignkey LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id2 FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT associated_id FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_foreign_id FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity', $auxiliary);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $auxiliary->id4);
+        $this->assertEquals('def', $inversed->id1);
+        $this->assertEquals('abc', $inversed->foreignEntity->id4);
+        $this->assertEquals('ghi', $owning->id2);
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase
+     */
+    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId()
+    {
+        $auxiliary = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'abc'
+        );
+
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => $auxiliary)
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity', $inversed);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity',
+            'ghi'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheEntityFromInversedToOwningIsEagerLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity',
+            array('id1' => 'def', 'foreignEntity' => 'abc')
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity', $inversed->associatedEntity);
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php
new file mode 100644
index 000000000..96daa6016
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToOne associations with composite id work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToOneCompositeIdTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetoone_compositeid');
+        parent::setUp();
+
+        $inversed = new Entity\InversedOneToOneCompositeIdEntity();
+        $inversed->id1 = 'abc';
+        $inversed->id2 = 'def';
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningOneToOneCompositeIdEntity();
+        $owning->id3 = 'ghi';
+
+        $inversed->associatedEntity = $owning;
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_onetoone_compositeid');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetoone_compositeid');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetoone_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_inversed_onetoone_compositeid LIMIT 1'));
+
+        $this->assertEquals('tuv', $conn->fetchColumn('SELECT id3 FROM vct_owning_onetoone_compositeid LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_id1 FROM vct_owning_onetoone_compositeid LIMIT 1'));
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT associated_id2 FROM vct_owning_onetoone_compositeid LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $inversed->id2);
+        $this->assertEquals('ghi', $owning->id3);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity',
+            'ghi'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheEntityFromInversedToOwningIsEagerLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity',
+            array('id1' => 'abc', 'id2' => 'def')
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity', $inversed->associatedEntity);
+    }
+}
diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php
new file mode 100644
index 000000000..15e45f9fe
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\ValueConversionType;
+
+use Doctrine\DBAL\Types\Type as DBALType;
+use Doctrine\Tests\Models\ValueConversionType as Entity;
+use Doctrine\Tests\OrmFunctionalTestCase;
+
+/**
+ * The entities all use a custom type that converst the value as identifier(s).
+ * {@see \Doctrine\Tests\DbalTypes\Rot13Type}
+ *
+ * Test that OneToOne associations work correctly.
+ *
+ * @group DDC-3380
+ */
+class OneToOneTest extends OrmFunctionalTestCase
+{
+    public function setUp()
+    {
+        $this->useModelSet('vct_onetoone');
+
+        parent::setUp();
+
+        $inversed = new Entity\InversedOneToOneEntity();
+        $inversed->id1 = 'abc';
+        $inversed->someProperty = 'some value to be loaded';
+
+        $owning = new Entity\OwningOneToOneEntity();
+        $owning->id2 = 'def';
+
+        $inversed->associatedEntity = $owning;
+        $owning->associatedEntity = $inversed;
+
+        $this->_em->persist($inversed);
+        $this->_em->persist($owning);
+
+        $this->_em->flush();
+        $this->_em->clear();
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $conn = static::$_sharedConn;
+
+        $conn->executeUpdate('DROP TABLE vct_owning_onetoone');
+        $conn->executeUpdate('DROP TABLE vct_inversed_onetoone');
+    }
+
+    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase()
+    {
+        $conn = $this->_em->getConnection();
+
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT id1 FROM vct_inversed_onetoone LIMIT 1'));
+
+        $this->assertEquals('qrs', $conn->fetchColumn('SELECT id2 FROM vct_owning_onetoone LIMIT 1'));
+        $this->assertEquals('nop', $conn->fetchColumn('SELECT associated_id FROM vct_owning_onetoone LIMIT 1'));
+    }
+
+    /**
+     * @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase
+     */
+    public function testThatEntitiesAreFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity',
+            'def'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity', $inversed);
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity', $owning);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity',
+            'abc'
+        );
+
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity',
+            'def'
+        );
+
+        $this->assertEquals('abc', $inversed->id1);
+        $this->assertEquals('def', $owning->id2);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheProxyFromOwningToInversedIsLoaded()
+    {
+        $owning = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity',
+            'def'
+        );
+
+        $inversedProxy = $owning->associatedEntity;
+
+        $this->assertEquals('some value to be loaded', $inversedProxy->someProperty);
+    }
+
+    /**
+     * @depends testThatEntitiesAreFetchedFromTheDatabase
+     */
+    public function testThatTheEntityFromInversedToOwningIsEagerLoaded()
+    {
+        $inversed = $this->_em->find(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity',
+            'abc'
+        );
+
+        $this->assertInstanceOf('Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity', $inversed->associatedEntity);
+    }
+}
diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php
index bc301ecfb..cfced6d61 100644
--- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php
+++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php
@@ -2,6 +2,7 @@
 
 namespace Doctrine\Tests;
 
+use Doctrine\DBAL\Types\Type;
 use Doctrine\Tests\EventListener\CacheMetadataListener;
 use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
 use Doctrine\ORM\Cache\DefaultCacheFactory;
@@ -199,6 +200,53 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
             'Doctrine\Tests\Models\Quote\Phone',
             'Doctrine\Tests\Models\Quote\User'
         ),
+        'vct_onetoone' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity'
+        ),
+        'vct_onetoone_compositeid' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity'
+        ),
+        'vct_onetoone_compositeid_foreignkey' => array(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity'
+        ),
+        'vct_onetomany' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity'
+        ),
+        'vct_onetomany_compositeid' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity'
+        ),
+        'vct_onetomany_compositeid_foreignkey' => array(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity'
+        ),
+        'vct_onetomany_extralazy' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToOneExtraLazyEntity'
+        ),
+        'vct_manytomany' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity'
+        ),
+        'vct_manytomany_compositeid' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity'
+        ),
+        'vct_manytomany_compositeid_foreignkey' => array(
+            'Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity',
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity'
+        ),
+        'vct_manytomany_extralazy' => array(
+            'Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity',
+            'Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity'
+        ),
     );
 
     /**
@@ -358,6 +406,68 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
             $conn->executeUpdate('DELETE FROM ' . $platform->quoteIdentifier("quote-user"));
         }
 
+        if (isset($this->_usedModelSets['vct_onetoone'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_onetoone');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetoone');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetoone_compositeid'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_onetoone_compositeid');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetoone_compositeid');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetoone_compositeid_foreignkey'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_onetoone_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetoone_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_auxiliary');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetomany'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_manytoone');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetomany');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetomany_compositeid'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_manytoone_compositeid');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetomany_compositeid');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetomany_compositeid_foreignkey'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_manytoone_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetomany_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_auxiliary');
+        }
+
+        if (isset($this->_usedModelSets['vct_onetomany_extralazy'])) {
+            $conn->executeUpdate('DELETE FROM vct_owning_manytoone_extralazy');
+            $conn->executeUpdate('DELETE FROM vct_inversed_onetomany_extralazy');
+        }
+
+        if (isset($this->_usedModelSets['vct_manytomany'])) {
+            $conn->executeUpdate('DELETE FROM vct_xref_manytomany');
+            $conn->executeUpdate('DELETE FROM vct_owning_manytomany');
+            $conn->executeUpdate('DELETE FROM vct_inversed_manytomany');
+        }
+
+        if (isset($this->_usedModelSets['vct_manytomany_compositeid'])) {
+            $conn->executeUpdate('DELETE FROM vct_xref_manytomany_compositeid');
+            $conn->executeUpdate('DELETE FROM vct_owning_manytomany_compositeid');
+            $conn->executeUpdate('DELETE FROM vct_inversed_manytomany_compositeid');
+        }
+
+        if (isset($this->_usedModelSets['vct_manytomany_compositeid_foreignkey'])) {
+            $conn->executeUpdate('DELETE FROM vct_xref_manytomany_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_owning_manytomany_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_inversed_manytomany_compositeid_foreignkey');
+            $conn->executeUpdate('DELETE FROM vct_auxiliary');
+        }
+
+        if (isset($this->_usedModelSets['vct_manytomany_extralazy'])) {
+            $conn->executeUpdate('DELETE FROM vct_xref_manytomany_extralazy');
+            $conn->executeUpdate('DELETE FROM vct_owning_manytomany_extralazy');
+            $conn->executeUpdate('DELETE FROM vct_inversed_manytomany_extralazy');
+        }
+
         $this->_em->clear();
     }
 
@@ -395,6 +505,8 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
      */
     protected function setUp()
     {
+        $this->setUpDBALTypes();
+
         $forceCreateTables = false;
 
         if ( ! isset(static::$_sharedConn)) {
@@ -582,4 +694,16 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
     {
         return count($this->_sqlLoggerStack->queries);
     }
+
+    /**
+     * Configures DBAL types required in tests
+     */
+    protected function setUpDBALTypes()
+    {
+        if (Type::hasType('rot13')) {
+            Type::overrideType('rot13', 'Doctrine\Tests\DbalTypes\Rot13Type');
+        } else {
+            Type::addType('rot13', 'Doctrine\Tests\DbalTypes\Rot13Type');
+        }
+    }
 }