public function hydrate($document, $data, $readOnly = false)
    {
        $metadata = $this->dm->getClassMetadata(get_class($document));
        // Invoke preLoad lifecycle events and listeners
        if (!empty($metadata->lifecycleCallbacks[Event::preLoad])) {
            $args = [new PreLoadEventArgs($document, $this->dm, $data)];
            $metadata->invokeLifecycleCallbacks(Event::preLoad, $document, $args);
        }
        if ($this->evm->hasListeners(Event::preLoad)) {
            $this->evm->dispatchEvent(Event::preLoad, new PreLoadEventArgs($document, $this->dm, $data));
        }

        // alsoLoadMethods may transform the document before hydration
        if (!empty($metadata->alsoLoadMethods)) {
            foreach ($metadata->alsoLoadMethods as $method => $attributeNames) {
                foreach ($attributeNames as $attributeName) {
                    // Invoke the method only once for the first attribute we find
                    if (array_key_exists($attributeName, $data)) {
                        $value = Type::convertUnmappedToPHPValue($data[$attributeName]);
                        $document->$method($value);
                        continue 2;
                    }
                }
            }
        }

        if ($document instanceof Proxy) {
            $document->__isInitialized__ = true;
            $document->__setInitializer(null);
            $document->__setCloner(null);
        }

        $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $readOnly);

        if ($document instanceof Proxy) {
            // lazy properties may be left uninitialized
            $properties = $document->__getLazyProperties();
            foreach ($properties as $propertyName => $property) {
                if (!isset($document->$propertyName)) {
                    $document->$propertyName = $properties[$propertyName];
                }
            }
        }

        // Invoke the postLoad lifecycle callbacks and listeners
        if (!empty($metadata->lifecycleCallbacks[Event::postLoad])) {
            $metadata->invokeLifecycleCallbacks(Event::postLoad, $document, [new LifecycleEventArgs($document, $this->dm)]);
        }
        if ($this->evm->hasListeners(Event::postLoad)) {
            $this->evm->dispatchEvent(Event::postLoad, new LifecycleEventArgs($document, $this->dm));
        }

        return $data;
    }