// 15 Essential Java Persistence API Interview Questions
The Java Persistence API is one of the most commonly used interface frameworks in the development world. Developers use it for database management from within their enterprise-level Java applications. Therefore, it is essential when building products that satisfy large-scale business needs.
There are tons of opportunities out there for Java developers since it is one of the most popular languages in the industry. However, many of these jobs depend on you possessing a solid knowledge of JPA. Therefore, you should know what questions you can face in interviews.
In this article, you'll see carefully curated JPA interview questions that help you cover all the crucial aspects of the API. Additionally, you get to learn from code examples and sharpen your Java skill set to ace interviews.
So read on!
Looking for Freelance Java Persistence API Developers? Build your product with Flexiple's top-quality talent.
Hire a Java Persistence API DeveloperHire NowThe @Query annotation enables you to execute SQL commands and save the resulting tables while staying within the Persistence API. You can execute JPQL commands with it as well.
Below is an example of how you can put the @Query annotation to use. Suppose you have a Student table and want to obtain a list of all students with a particular first name. You can retrieve such students and associate them with a function having a parameter using the following code.
@Query("select s from Student s where s.first_name = ?1")
List getStudents(String first_name);
The function will execute the command, save the results, and return a list of Student objects.
With the metamodel class of an entity, you can access the metadata of managed entity classes and access the attributes' names as strings while writing any criteria queries. This way, you don't have to look up the names of the entity's information and rewrite queries for any name changes.
JPA provides you the functionality to generate the metamodel automatically. The resulting metamodel has the same name as the entity class with an underscore at the end. In the case of the given entity class, the static metamodel will look as shown below.
@StaticMetamodel(AutoManufacturer.class)
public abstract class AutoManufacturer_ {
public static volatile SingularAttribute name;
public static volatile SingularAttribute headquarters_country;
public static volatile SingularAttribute id;
public static volatile SingularAttribute cars;
}
An ElementCollection is a convenient data structure to handle non-standard relationships in JPA. It is most commonly used when defining a one-to-many relationship to an Embeddable object.
In the given example, we see that the Employee table and the Phone table have a one-to-many relationship.
An ElementCollection is helpful here since a single Employee can have multiple phone numbers and they can be Embeddable objects.
The following code shows how we specify the ElementCollection for these tables.
@Entity
public class Employee {
@Id
@Column(name="EMP_ID")
private long id;
...
@ElementCollection
@CollectionTable(
name="PHONE",
[email protected](name="OWNER_ID")
)
@Column(name="PHONE_NUMBER")
private List phones;
...
}
Here, in the Employee table, we specify that the attribute we need to join the tables on, is the OWNER_ID column.
Note: Commonly asked JPA interview questionWe can map attributes from the Entity class to the Embeddable class and override the column properties of the latter by using the @AttributeOverrides and @AttributeOverride annotations.
We can insert the following piece of code in the Team entity, and this would map the relevant attributes.,/p>
@Embedded
@AttributeOverrides({
@AttributeOverride( name = "firstName", column = @Column(name = "teamlead_first_name")),
@AttributeOverride( name = "lastName", column = @Column(name = "teamlead_last_name")),
@AttributeOverride( name = "phone", column = @Column(name = "teamlead_phone"))
})
private TeamLead teamLead;
To make an entity persist, we need to create an EntityManager object for it. We can easily do that by first creating an EntityManagerFactory and constructing an EntityManager through it.
For example, the following code creates an EntityManagerFactory for the Employee entity through the interface provided in the java.persistance package.
EntityManagerFactory emf=Persistence.createEntityManagerFactory("Employee");
Next, we will get an EntityManager from this factory and initialize a transaction through it.
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
After this, we persist whatever data we wish, into the database. In our case, it is an Employee object e_obj.
em.persist(e_obj);
Finally, we end the transaction and free the factory.
em.getTransaction().commit();
emf.close();
em.close();
In object-oriented programming, child entities inheriting from the parent entity is the most basic form of inheritance. Within JPA, you have to use the MappedSuperclass strategy for such inheritance. At the database level, this model is invisible as the properties from the base class simply copy over to the table mapped by the entity class.
In the case given above, the class definitions would look like the ones shown below.
@MappedSuperclass
public static class Person {
@Id
private Long id;
private String Name;
private Integer Age;
private Integer Size;
private String HairColor;
private void Walk(Distance) {
//implementation
}
private void Eat(Food) {
//implementation
}
private void Speak(Language) {
//implementation
}
}
@Entity(name = "Student")
public static class Student extends Person {
private void Study(Subject) {
//implementation
}
@Entity(name = "Teacher")
public static class Teacher extends Person {
private void Study(Subject) {
//implementation
}
Here, we specify the MappedSuperclass inheritance with its special annotation directly above the Person class. The other two classes simply extend this Person class. Additionally, they hold methods and attributes unique to them.
Note: Commonly asked JPA interview questionA persistence-unit involves information about aspects like the EntityManagerFactory’s configuration, the set of entity classes, and the mapping metadata. Whenever you load up an EntityManagerFactory instance, a persistence-unit will load up.
On the other hand, a persistence-context is tied to an EntityManger instance and is a set of managed unique entity instances. The EntityManager works with the context to manage the entities and the lifecycle of their instances.
The annotations for each will inject a special instance of an EntityManagerFactory and an EntityManager, respectively. The following code shows how we use each annotation.
@PersistenceContext
private EntityManager entityManager;
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
The above lines specify that the EntityManager object needs to use the transactional persistence context below. The last couple of lines show that the EntityManagerFactory needs to be injected as a dependency for any processing.
Rollbacks happen when transactions don’t go through completely and some part (or even the whole transaction) fails for some reason. In that case, you want all the changes made by the transaction to be undone.
As shown in the code below, you can easily make your transactional method rollback when it encounters an exception.
@Transactional
(rollbackFor = Exception.class,
noRollbackFor = EntityNotFoundException.class)
public void updateCarName(String newName) {
Car car = carRepository.findById(1L).get();
car.setName(newName);
}
The code above shows that the transaction should roll back if the method faces any exception during execution. With the noRollbackFor variable, we specify that it shouldn’t roll back if it doesn’t find an entity to update.
The given test involves inserting an entity and then testing if the action was successful by retrieving its id within the database. However, since insertion into the database is a single transaction, we need to clarify that this is a transactional activity.
In JPA, we do that through the @Transactional annotation, which is missing here. Since the @Transactional annotation is missing, the call to persist the Student entity fails.We use native queries when we wish to directly write queries in the database’s language and not in JPQL.
It is the go-to option in case:
- JPQL is unavailable or
- using the native query language helps performance
Eventually, JPQL is anyways translated into the native query language and the conversion can affect performance, even if by a tiny margin.
The following code shows how we can create a native query to fetch the relevant data as asked in the question.
@Repository
public interface ProductRepository extends JpaRepository {
@Query(value = "SELECT name, company, type FROM products WHERE price = :maxPrice", nativeQuery = true)
public List getProductsWithMaxPrice(Integer maxPrice);
}
Note: Commonly asked JPA interview questionUsing the EntityManager methods, persist() and merge(), depends on what you wish to do with the entity once you have added it to the PersistanceContext.
If you simply wish to add an entity to the context and make sure that any future updates to it are tracked, persist() is the straightforward method. However, merge() copies the state of your entity, and returns a managed copy. In that case, the passed instance is not managed, unless merge() is called again. Although you can use the managed instance which merge() returns.
The following code example shows the difference in implementation.
DemoEntity e = new DemoEntity(); // using persist()
em.persist(e);
e.setSomeDemoField(someDemoValue);
// the setSomeDemoField in the database is updated
e = new DemoEntity();
DemoEntity e2 = em.merge(e);
e2.setSomeDemoField(someDemoValue);
// the row for setSomeDemoField gets updated but changes are made to e2 and not e
Note: Commonly asked JPA interview questionA transactional method is a function that follows the ACID principles. Any method that includes inserting, deleting, or updating the database needs to be transactional so that the data is consistent and activity associated with it is recorded and tracked. You can specify a transactional method using the @Transactional annotation above its definition.
For the given case, let us call the required transactional method as updateCarName. Then, the method will look as below.
@Transactional
public void updateCarName(String newName) {
Car car = carRepository.findById(1L).get();
car.setName(newName);
}
Note: Commonly asked JPA interview questionNamedQueries are queries that are static and are given a meaningful name. You create a single named-query through the @NamedQuery annotation and bundle them under the @NamedQueries annotation. The queries are inserted with other annotations above the entity definition.
For the given case, the named queries would look like below.@NamedQuery(name = "Car.findByName", query = "select c from Car c where c.name = ?1")
@NamedQuery(name = "Car.findByModelNameAndModelYear", query = "select c from Car c where c.name = ?1 and c.rollNumber = ?2")
@NamedQuery(name = "Car.findByModelNameOrModelYear", query = "select c from Car c where c.name = ?1 or c.rollNumber = ?2")
Note: Commonly asked JPA interview question
The PersistenceException object gets thrown when any activity related to the Persistence context faces errors.
The following example shows a method throwing this exception if its rollback of a transaction fails. The failure will throw anexception that the piece of code catches. Then, it turns it into a specific PersistanceException.
public void rollbackTransaction() {
try {
transaction.rollback();
}
catch ( final Exception e) {
throw new PersistenceException("Rollback failed", e);
}
}
Through FetchType, you can specify how you want JPA to fetch related entities in a database. The API allows this specification so that you don’t unnecessarily fetch objects or get objects not relevant to your use case. It adds to the performance of the application too.
In the following snippet, we specify that we want our Course entities in the database fetched in a LAZY manner.
@Entity
public class Student {
@Id
private String id;
private String name;
private String department_name;
@OneToMany(fetch = FetchType.LAZY)
private List courses;
}
The API will get the entity objects only when we need them or use them. The initialization of the relationship is delayed.
Another specification of using FetchType is in the EAGER manner. Then, the API will fetch all related entities in the first query, and in most cases, it is very efficient. However, you may have many entities that you simply don’t need.