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.
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 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.
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.
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.
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...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With