diff --git a/persistence-modules/java-jpa-2/pom.xml b/persistence-modules/java-jpa-2/pom.xml
index 76b1033dac..ab5bb39dfc 100644
--- a/persistence-modules/java-jpa-2/pom.xml
+++ b/persistence-modules/java-jpa-2/pom.xml
@@ -48,6 +48,17 @@
             ${postgres.version}
             runtime
         
+        
+            com.querydsl
+            querydsl-apt
+            ${querydsl.version}
+            provided
+        
+        
+            com.querydsl
+            querydsl-jpa
+            ${querydsl.version}
+        
 
         
             org.assertj
@@ -101,16 +112,33 @@
                         
                             
                                 target/metamodel
+                                ${project.build.directory}/generated-sources/java/
                             
                         
                     
                 
             
+            
+                com.mysema.maven
+                apt-maven-plugin
+                1.1.3
+                
+                    
+                        
+                            process
+                        
+                        
+                            target/generated-sources/java
+                            com.querydsl.apt.jpa.JPAAnnotationProcessor
+                        
+                    
+                
+            
         
     
 
     
-        5.4.0.Final
+        5.4.14.Final
         2.7.4
         42.2.5
         2.2
@@ -118,6 +146,7 @@
         3.5.1
         3.3.3
         3.0.0
+        4.3.1
     
 
-
\ No newline at end of file
+
diff --git a/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Cocktail.java b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Cocktail.java
new file mode 100644
index 0000000000..96310c1cc5
--- /dev/null
+++ b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Cocktail.java
@@ -0,0 +1,90 @@
+package com.baeldung.jpa.unrelated.entities;
+
+import org.hibernate.annotations.Fetch;
+import org.hibernate.annotations.FetchMode;
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
+
+import javax.persistence.*;
+import java.util.List;
+import java.util.Objects;
+
+@Entity
+@Table(name = "menu")
+public class Cocktail {
+    @Id
+    @Column(name = "cocktail_name")
+    private String name;
+
+    @Column
+    private double price;
+
+    @Column(name = "category")
+    private String category;
+
+    @OneToOne
+    @NotFound(action = NotFoundAction.IGNORE)
+    @JoinColumn(name = "cocktail_name",
+        referencedColumnName = "cocktail", 
+        insertable = false, updatable = false, 
+        foreignKey = @javax.persistence
+          .ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
+    private Recipe recipe;
+
+    @OneToMany(fetch = FetchType.LAZY)
+    @NotFound(action = NotFoundAction.IGNORE)
+    @JoinColumn(
+        name = "cocktail", 
+        referencedColumnName = "cocktail_name", 
+        insertable = false, 
+        updatable = false, 
+        foreignKey = @javax.persistence
+          .ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
+    private List recipeList;
+
+    public Cocktail() {
+    }
+
+    public Cocktail(String name, double price, String baseIngredient) {
+        this.name = name;
+        this.price = price;
+        this.category = baseIngredient;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public double getPrice() {
+        return price;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public Recipe getRecipe() {
+        return recipe;
+    }
+
+    public List getRecipeList() {
+        return recipeList;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Cocktail cocktail = (Cocktail) o;
+        return Double.compare(cocktail.price, price) == 0 && 
+            Objects.equals(name, cocktail.name) && 
+            Objects.equals(category, cocktail.category);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, price, category);
+    }
+}
diff --git a/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/MultipleRecipe.java b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/MultipleRecipe.java
new file mode 100644
index 0000000000..8664d6fd7f
--- /dev/null
+++ b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/MultipleRecipe.java
@@ -0,0 +1,71 @@
+package com.baeldung.jpa.unrelated.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Objects;
+
+@Entity
+@Table(name = "multiple_recipes")
+public class MultipleRecipe {
+    @Id
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "cocktail")
+    private String cocktail;
+
+    @Column(name = "instructions")
+    private String instructions;
+
+    @Column(name = "base_ingredient")
+    private String baseIngredient;
+
+    public MultipleRecipe() {
+    }
+
+    public MultipleRecipe(Long id, String cocktail, 
+        String instructions, String baseIngredient) {
+        this.id = id;
+        this.cocktail = cocktail;
+        this.instructions = instructions;
+        this.baseIngredient = baseIngredient;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getCocktail() {
+        return cocktail;
+    }
+
+    public String getInstructions() {
+        return instructions;
+    }
+
+    public String getBaseIngredient() {
+        return baseIngredient;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        MultipleRecipe that = (MultipleRecipe) o;
+        
+        return Objects.equals(id, that.id) && 
+          Objects.equals(cocktail, that.cocktail) && 
+          Objects.equals(instructions, that.instructions) && 
+          Objects.equals(baseIngredient, that.baseIngredient);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, cocktail, 
+          instructions, baseIngredient);
+    }
+}
diff --git a/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Recipe.java b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Recipe.java
new file mode 100644
index 0000000000..4b3d200b60
--- /dev/null
+++ b/persistence-modules/java-jpa-2/src/main/java/com/baeldung/jpa/unrelated/entities/Recipe.java
@@ -0,0 +1,50 @@
+package com.baeldung.jpa.unrelated.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Objects;
+
+@Entity
+@Table(name="recipes")
+public class Recipe {
+    @Id
+    @Column(name = "cocktail")
+    private String cocktail;
+
+    @Column
+    private String instructions;
+
+    public Recipe() {
+    }
+
+    public Recipe(String cocktail, String instructions) {
+        this.cocktail = cocktail;
+        this.instructions = instructions;
+    }
+
+    public String getCocktail() {
+        return cocktail;
+    }
+
+    public String getInstructions() {
+        return instructions;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Recipe recipe = (Recipe) o;
+        return Objects.equals(cocktail, recipe.cocktail) 
+          && Objects.equals(instructions, recipe.instructions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cocktail, instructions);
+    }
+}
diff --git a/persistence-modules/java-jpa-2/src/main/resources/META-INF/persistence.xml b/persistence-modules/java-jpa-2/src/main/resources/META-INF/persistence.xml
index 11e007973f..eec7f7cf6e 100644
--- a/persistence-modules/java-jpa-2/src/main/resources/META-INF/persistence.xml
+++ b/persistence-modules/java-jpa-2/src/main/resources/META-INF/persistence.xml
@@ -162,5 +162,26 @@
                       value="false" />
         
     
-
-
\ No newline at end of file
+    
+    
+        org.hibernate.jpa.HibernatePersistenceProvider
+        com.baeldung.jpa.unrelated.entities.Cocktail
+		com.baeldung.jpa.unrelated.entities.Recipe
+		com.baeldung.jpa.unrelated.entities.MultipleRecipe
+        true
+        
+            
+            
+            
+            
+            
+            
+            
+            
+        
+    
+
diff --git a/persistence-modules/java-jpa-2/src/test/java/com/baeldung/jpa/unrelated/entities/UnrelatedEntitiesUnitTest.java b/persistence-modules/java-jpa-2/src/test/java/com/baeldung/jpa/unrelated/entities/UnrelatedEntitiesUnitTest.java
new file mode 100644
index 0000000000..044e59b16e
--- /dev/null
+++ b/persistence-modules/java-jpa-2/src/test/java/com/baeldung/jpa/unrelated/entities/UnrelatedEntitiesUnitTest.java
@@ -0,0 +1,189 @@
+package com.baeldung.jpa.unrelated.entities;
+
+import javax.persistence.*;
+
+import com.querydsl.jpa.impl.JPAQuery;
+import org.junit.jupiter.api.*;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class UnrelatedEntitiesUnitTest {
+    private static EntityManagerFactory entityManagerFactory;
+    private static EntityManager entityManager;
+    private static Cocktail mojito;
+    private static Cocktail ginTonic;
+
+    @BeforeAll
+    public static void setup() {
+        entityManagerFactory = Persistence.createEntityManagerFactory("jpa-h2-unrelated-entities");
+        entityManager = entityManagerFactory.createEntityManager();
+        mojito = new Cocktail("Mojito", 11, "Rum");
+        ginTonic = new Cocktail("Gin tonic", 8.50, "Gin");
+        entityManager.getTransaction().begin();
+        entityManager.persist(mojito);
+        entityManager.persist(ginTonic);
+        entityManager.persist(new Recipe(mojito.getName(), "Some instructions"));
+        entityManager.persist(new MultipleRecipe(1L, mojito.getName(), 
+          "some instructions", mojito.getCategory()));
+        entityManager.persist(new MultipleRecipe(2L, mojito.getName(), 
+          "some other instructions", mojito.getCategory()));
+        entityManager.getTransaction().commit();
+    }
+
+    @AfterAll
+    public static void closeSession() {
+        entityManager.close();
+    }
+
+
+    @Test
+    public void givenCocktailsWithRecipe_whenQuerying_thenTheExpectedCocktailsReturned() {
+        // JPA
+        Cocktail cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c join c.recipe", Cocktail.class)
+          .getSingleResult();
+        verifyResult(mojito, cocktail);
+
+        cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c join Recipe r "
+          + "on c.name = r.cocktail", Cocktail.class)
+          .getSingleResult();
+        verifyResult(mojito, cocktail);
+
+        // QueryDSL
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .join(QCocktail.cocktail.recipe)
+          .fetchOne();
+        verifyResult(mojito, cocktail);
+
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .join(QRecipe.recipe)
+          .on(QCocktail.cocktail.name.eq(QRecipe.recipe.cocktail))
+          .fetchOne();
+        verifyResult(mojito, cocktail);
+    }
+
+    @Test
+    public void givenCocktailsWithoutRecipe_whenQuerying_thenTheExpectedCocktailsReturned() {
+        Cocktail cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c left join c.recipe r " 
+          + "where r is null", Cocktail.class)
+          .getSingleResult();
+        verifyResult(ginTonic, cocktail);
+
+        cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c left join Recipe r " 
+          + "on c.name = r.cocktail " 
+          + "where r is null", Cocktail.class)
+          .getSingleResult();
+        verifyResult(ginTonic, cocktail);
+
+        QRecipe recipe = new QRecipe("alias");
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .leftJoin(QCocktail.cocktail.recipe, recipe)
+          .where(recipe.isNull())
+          .fetchOne();
+        verifyResult(ginTonic, cocktail);
+
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .leftJoin(QRecipe.recipe)
+          .on(QCocktail.cocktail.name.eq(QRecipe.recipe.cocktail))
+          .where(QRecipe.recipe.isNull())
+          .fetchOne();
+        verifyResult(ginTonic, cocktail);
+    }
+
+    @Test
+    public void givenCocktailsWithMultipleRecipes_whenQuerying_thenTheExpectedCocktailsReturned() {
+        // JPQL
+        Cocktail cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c join c.recipeList", Cocktail.class)
+          .getSingleResult();
+        verifyResult(mojito, cocktail);
+
+        cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c join MultipleRecipe mr "
+          + "on mr.cocktail = c.name", Cocktail.class)
+          .getSingleResult();
+        verifyResult(mojito, cocktail);
+
+        // QueryDSL
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .join(QCocktail.cocktail.recipeList)
+          .fetchOne();
+        verifyResult(mojito, cocktail);
+
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .join(QMultipleRecipe.multipleRecipe)
+          .on(QCocktail.cocktail.name.eq(QMultipleRecipe.multipleRecipe.cocktail))
+          .fetchOne();
+        verifyResult(mojito, cocktail);
+    }
+
+    @Test
+    public void givenCocktailsWithoutMultipleRecipes_whenQuerying_thenTheExpectedCocktailsReturned() {
+        // JPQL
+        Cocktail cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c left join c.recipeList r "
+          + "where r is null", Cocktail.class)
+          .getSingleResult();
+        verifyResult(ginTonic, cocktail);
+
+        cocktail = entityManager.createQuery("select c "
+          + "from Cocktail c left join MultipleRecipe r " 
+          + "on c.name = r.cocktail "
+          + "where r is null", Cocktail.class)
+          .getSingleResult();
+        verifyResult(ginTonic, cocktail);
+
+        // QueryDSL
+        QMultipleRecipe multipleRecipe = new QMultipleRecipe("alias");
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .leftJoin(QCocktail.cocktail.recipeList, multipleRecipe)
+          .where(multipleRecipe.isNull())
+          .fetchOne();
+        verifyResult(ginTonic, cocktail);
+
+        cocktail = new JPAQuery(entityManager).from(QCocktail.cocktail)
+          .leftJoin(QMultipleRecipe.multipleRecipe)
+          .on(QCocktail.cocktail.name.eq(QMultipleRecipe.multipleRecipe.cocktail))
+          .where(QMultipleRecipe.multipleRecipe.isNull())
+          .fetchOne();
+        verifyResult(ginTonic, cocktail);
+    }
+
+    @Test
+    public void givenMultipleRecipesWithCocktails_whenQuerying_thenTheExpectedMultipleRecipesReturned() {
+        Consumer> verifyResult = recipes -> {
+            assertEquals(2, recipes.size());
+            recipes.forEach(r -> assertEquals(mojito.getName(), r.getCocktail()));
+        };
+
+        // JPQL
+        List recipes = entityManager.createQuery("select distinct r "
+          + "from MultipleRecipe r "
+          + "join Cocktail c " 
+          + "on r.baseIngredient = c.category",
+          MultipleRecipe.class).getResultList();
+        
+        verifyResult.accept(recipes);
+
+        // QueryDSL
+        QCocktail cocktail = QCocktail.cocktail;
+        QMultipleRecipe multipleRecipe = QMultipleRecipe.multipleRecipe;
+        recipes = new JPAQuery(entityManager).from(multipleRecipe)
+          .join(cocktail)
+          .on(multipleRecipe.baseIngredient.eq(cocktail.category))
+          .fetch();
+        
+        verifyResult.accept(recipes);
+    }
+
+    private void verifyResult(Cocktail expectedCocktail, Cocktail queryResult) {
+        assertNotNull(queryResult);
+        assertEquals(expectedCocktail, queryResult);
+    }
+}