Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static Metamodel attributes null when unit testing

Tags:

java

jpa

testng

I have @Entity classes in an external package that also have static metamodels. In my application's service class, I am using those metamodels and the EntityManager/CriteriaBuilder/CriteriaQuery to retrieve my data. This works fine when running the application. However, when running unit tests, my metamodels and their attributes are always null.

Code...

package com.example.core.entities;

@Entity
@Table(schema = "lookup", name="BookingSystem")
public class BookingSystem implements ILookupEntity, IAuditEntity, Serializable {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Column(name = "id")
  public Integer id;

  @Column(name = "name")
  public String name;

  @Column(name = "code")
  public Integer code;
}

package com.example.core.entities;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(BookingSystem.class)
public abstract class BookingSystem_ {
  public static volatile SingularAttribute<BookingSystem, Integer> id;
  public static volatile SingularAttribute<BookingSystem, Integer> code;
  public static volatile SingularAttribute<BookingSystem, String> name;
}

Usage in my app's service class...

package com.example.bookingsystem;

@Service
public class BookingService {
  @PersistenceContext
  private EntityManager entityManager;

public void saveBooking(Booking booking) {
//...
  RepositoryQueryBuilder<BookingSystem> bookingSystemSelector = new RepositoryQueryBuilder<>(entityManager, BookingSystem.class);
  List<BookingSystem> bookingSystems = bookingSystemSelector
      .and(BookingSystem_.code, booking.bookingSystem.code) //<-- Here "BookingSystem_.code" is null.
      .getResultList();
  //...
  }
}

The "RepositoryQueryBuilder" class is just a utility builder class that wraps an EntityManager, CriteriaBuilder, etc. Basically modeled after this example... JPA Criteria Predicate Conditions

Unit test code...

package com.example.bookingsystem;

public abstract class BaseTestSetup {
  @InjectMocks
  protected BookingService bookingService;

  protected EntityManager entityManager = PowerMockito.mock(EntityManager.class);
  protected CriteriaBuilder criteriaBuilder = PowerMockito.mock(CriteriaBuilder.class);
  protected CriteriaQuery<BookingSystem> criteriaQuery = PowerMockito.mock(CriteriaQuery.class);
  protected Root<BookingSystem> root = PowerMockito.mock(Root.class);

  protected void arrange() {
    when(entityManager.getCriteriaBuilder()).thenReturn(criteriaBuilder);
    when(criteriaBuilder.createQuery(BookingSystem.class)).thenReturn(criteriaQuery);
    when(criteriaQuery.from(Matchers.<Class<BookingSystem>>any())).thenReturn(root);
    when(criteriaQuery.from(Matchers.<EntityType<BookingSystem>>any())).thenReturn(root);
}

}

@RunWith(PowerMockRunner.class)
public class BookingServiceTest extends BaseTestSetup {
  @BeforeClass
  @Override
  public void arrange() {
    super.arrange();

    //...
}

@Test
public void doIt() {
    Booking booking = new Booking();
    booking.id = 12345;
    booking.bookingSystem = new BookingSystem();
    booking.bookingSystem.id = 1;
    booking.bookingSystem.code = 106000;

    bookingService.saveBooking(booking);
  }
}

I've looked at this JPA/Hibernate Static Metamodel Attributes not Populated -- NullPointerException, but the solution seems to be "make sure that the entity and its metamodel are in the same package", but as you can see, both are already in my "com.example.core.entities" package.

I'm using all bean and annotation driven configruation in my code (no persistence or context xml files). As far as testing goes, I'm using TestNG and PowerMock from within IntelliJ.

It just seems as if the metamodels aren't being picked up during unit tests. Any ideas.

like image 386
HenryC Avatar asked Mar 25 '16 18:03

HenryC


People also ask

What is static metamodel?

A static metamodel is a series of classes that "mirror" the entities and embeddables in the domain model and provide static access to the metadata about the mirrored class's attributes.

What is JPA static metamodel?

What is it about? JPA 2 defines a typesafe Criteria API which allows Criteria queries to be constructed in a strongly-typed manner, utilizing so called static metamodel classes. For developers it is important that the task of the metamodel generation can be automated.

What is metamodel in Java?

The Metamodel API is used to create a metamodel of the managed entities in a particular persistence unit. For each entity class in a particular package, a metamodel class is created with a trailing underscore and with attributes that correspond to the persistent fields or properties of the entity class.


2 Answers

Instead of creating own class, I suggest making Mockito to do the job for you.

 @Mock // declare a mock along the others you might have
 private SingularAttribute<BookingSystem, Integer> code;

 @Before
 public void setUp() throws Exception { 
     // fill metamodel with it
     BookingSystem_.code = code;
 }

Worth to mention however that bringing metamodels to service layer is not very good practice, you would rather push them down to DAO methods.

like image 174
no id Avatar answered Sep 20 '22 08:09

no id


The static metamodel classes are populated when hibernate is loaded. So, either you configure hibernate context in your test or you populate the attributes manually before the method execution. In you code, you could do:

@Test
public void doIt() {
    BookingSystem_.code = new SingularAttributeMock<BookingSystem, Integer>();
    bookingService.saveBooking(booking);
  }
}

The class SingularAttributeMock can be created custom-made in order to use it in your tests. You can also use any other implementation of the SingularAttribute class.

public class SingularAttributeMock<X, Y> implements SingularAttribute<X, Y> {
   //Overriding methods of SingularAttribute...
}
like image 32
Andrés Cuadros Avatar answered Sep 21 '22 08:09

Andrés Cuadros