Using native Queries with Hibernate and JPA

JPA native queries allow developers to use native SQL statements in their JPA application. You can use these queries to access specific features of a database or to optimize performance by using the database’s native language.

One advantage of using native queries is the ability to access database-specific features that may not be supported by JPA’s query language. For example, a developer may want to use a stored procedure or a specific type of join that is only available in the database’s native language.

Native queries can also be useful for optimizing performance. JPA’s query language is portable across different databases. However, this can sometimes result in less efficient queries. By using native queries, developers can take advantage of features specific to their database that may result in faster query execution.

There are two ways to use native queries in JPA: through the @NamedNativeQuery annotation or by using the createNativeQuery method of the EntityManager.

Using the createNativeQuery method

The createNativeQuery method of the EntityManager allows developers to create and execute a native query on the fly. This method can be useful when you are using a Native query just once. Also, you can use it to generate the native query dynamically. For example:

public List<Customer> findAllCustomersNative() {
        Query query = em.createNativeQuery("SELECT * from customer",Customer.class);
        List<Customer> customerList = query.getResultList();
        return customerList;
}

Using the @NamedNativeQuery annotation

The @NamedNativeQuery annotation allows developers to define a native query and give it a name. You can then use the query in the application by calling the createNamedQuery method of the EntityManager. This method of using native queries is useful when you are using them from several parts of your application.

For example:

@Entity
@NamedNativeQueries({

    @NamedNativeQuery(
        name = "Person.findAllPersons",
        query =
            "SELECT * " +
            "FROM Person ", resultClass = Person.class
    ),

    @NamedNativeQuery(
        name = "Person.findPersonByName",
        query =
            "SELECT * " +
            "FROM Person p " +
            "WHERE p.name = ?", resultClass = Person.class)
    
})
public class Person {


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    String name;
    String surname;


    // Getter and Setters omitted for brevity

}

In this example, we have the following @NamedNativeQuery:

  • Person.findAllPersons: to return all Person Entities
  • Person.findPersonByName: to return all Person Entities filtered by name

Like JP QL named queries, the name of the query must be unique within the persistence unit. If the result type is an Entity, the resultClass element may be used to indicate the Entity class. The following section will teach you how to perform a custom mapping of your ResultSetMapping.

Performing Custom Mappings of Native Queries

In the examples so far, the resultClass of the Native Query matches with the fields we return from the Query.

To use a JPA native query with a custom Java object, you must first define a result set mapping using the @SqlResultSetMapping annotation. This annotation allows you to specify the mapping between the Query results and the target object(s).

To map the query results to a custom Java object, you can use the @ConstructorResult annotation in the @SqlResultSetMapping annotation. This annotation specifies a constructor in the custom object to be used to create an instance of the object from the query results.

For example, suppose you have a custom Java object called EmployeeSummary that has the following constructor:

public EmployeeSummary(int id, String name, double salary)

To map the query results to this object, you could use the following

@SqlResultSetMapping:

  @SqlResultSetMapping(
    name = "EmployeeSummaryMapping",
    classes = {
      @ConstructorResult(
        targetClass = EmployeeSummary.class,
        columns = {
          @ColumnResult(name = "emp_id"),
          @ColumnResult(name = "emp_name"),
          @ColumnResult(name = "emp_salary")
        }
      )
    }
  )

You can then use this result set mapping in a named native query by specifying it in the @NamedNativeQuery annotation:

@NamedNativeQuery(
name="findEmployeeSummaries",
query="SELECT * FROM employees",
resultSetMapping="EmployeeSummaryMapping"
)

To execute the named native query, you can use the createNamedQuery method of the EntityManager and pass in the query name. The results of the query will map the EmployeeSummary object using the constructor available in the @ConstructorResult annotation.

It is important to note that the @SqlResultSetMapping annotation can only be used with native queries and cannot be used with JPA’s query language.

Conclusion

Overall, JPA native queries can be a useful tool for developers looking to access specific database features or optimize performance. However, it is important to weigh the benefits against the potential drawbacks and consider the long-term maintenance of the application.

Found the article helpful? if so please follow us on Socials