blob: f0c9f68b2eabe20221fa5f505f83a9e168bc418b [file] [log] [blame]
/*
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 10/09/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 10/25/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 11/22/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (index metadata support)
// 11/28/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 12/07/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
// 01/23/2013-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 02/28/2013-2.5 Chris Delahunt
// - 402029: Application exceptions need to be wrapped in PersistenceException
// 06/03/2013-2.5.1 Guy Pelletier
// - 402380: 3 jpa21/advanced tests failed on server with
// "java.lang.NoClassDefFoundError: org/eclipse/persistence/testing/models/jpa21/advanced/enums/Gender"
// 07/16/2013-2.5.1 Guy Pelletier
// - 412384: Applying Converter for parameterized basic-type for joda-time's DateTime does not work
// 11/06/2014-2.6 Tomas Kraus
// - 449818: Test to verify Convert annotation on ElementCollection of Embeddable class.
package org.eclipse.persistence.testing.tests.jpa21.advanced;
import java.util.Date;
import java.util.List;
import java.util.Map;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.mappings.converters.ConverterClass;
import org.eclipse.persistence.mappings.converters.SerializedObjectConverter;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.testing.models.jpa21.advanced.Employee;
import org.eclipse.persistence.testing.models.jpa21.advanced.Organizer;
import org.eclipse.persistence.testing.models.jpa21.advanced.Race;
import org.eclipse.persistence.testing.models.jpa21.advanced.Responsibility;
import org.eclipse.persistence.testing.models.jpa21.advanced.Runner;
import org.eclipse.persistence.testing.models.jpa21.advanced.RunnerInfo;
import org.eclipse.persistence.testing.models.jpa21.advanced.RunnerStatus;
import org.eclipse.persistence.testing.models.jpa21.advanced.RunnerVictory;
import org.eclipse.persistence.testing.models.jpa21.advanced.converters.CompetitionConverter;
import org.eclipse.persistence.testing.models.jpa21.advanced.converters.ResponsibilityConverter;
import org.eclipse.persistence.testing.models.jpa21.advanced.enums.Health;
import org.eclipse.persistence.testing.models.jpa21.advanced.enums.Level;
import org.eclipse.persistence.testing.models.jpa21.advanced.enums.RunningStatus;
public class ConverterTestSuite extends JUnitTestCase {
public ConverterTestSuite() {}
public ConverterTestSuite(String name) {
super(name);
setPuName("MulitPU-1");
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.setName("ConverterTestSuite");
suite.addTest(new ConverterTestSuite("testAutoApplyConverter"));
suite.addTest(new ConverterTestSuite("testAnnotationConverters"));
suite.addTest(new ConverterTestSuite("testConverterExceptionWrapping1"));
suite.addTest(new ConverterTestSuite("testConverterExceptionWrapping2"));
suite.addTest(new ConverterTestSuite("testConvertOnElementCollection"));
return suite;
}
/**
* Test that an attribute picks up an auto apply converter.
*/
public void testAutoApplyConverter() {
ServerSession session = getPersistenceUnitServerSession();
ClassDescriptor employeeDescriptor = session.getDescriptor(Employee.class);
DirectToFieldMapping salaryMapping = (DirectToFieldMapping) employeeDescriptor.getMappingForAttributeName("salary");
assertNotNull("Salary mapping did not have the auto apply converter", salaryMapping.getConverter());
DirectToFieldMapping previousSalaryMapping = (DirectToFieldMapping) employeeDescriptor.getMappingForAttributeName("previousSalary");
assertNull("Salary mapping did not have the auto apply converter", previousSalaryMapping.getConverter());
ClassDescriptor runnerDescriptor = session.getDescriptor(Runner.class);
DirectToFieldMapping tagsMapping = (DirectToFieldMapping) runnerDescriptor.getMappingForAttributeName("tags");
assertTrue("Tags mapping did not have a converter", tagsMapping.hasConverter());
assertTrue("Serials mappings did not have a SerializedObjectConverter", tagsMapping.getConverter() instanceof ConverterClass);
DirectToFieldMapping serialsMapping = (DirectToFieldMapping) runnerDescriptor.getMappingForAttributeName("serials");
assertTrue("Serials mapping did not have a converter", serialsMapping.hasConverter());
assertTrue("Serials mappings did not have a SerializedObjectConverter", serialsMapping.getConverter() instanceof SerializedObjectConverter);
}
/**
* Test that converters are set.
*/
public void testAnnotationConverters() {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Runner runner = new Runner();
runner.setAge(53);
runner.setIsFemale();
runner.setFirstName("Doris");
runner.setLastName("Day");
runner.addPersonalBest("10 KM", "47:34");
runner.addPersonalBest("5", "26:41");
runner.addTag("tag1");
runner.addAccomplishment("Ran 100KM without stopping", new Date(System.currentTimeMillis()));
RunnerInfo runnerInfo = new RunnerInfo();
runnerInfo.setHealth(Health.H);
runnerInfo.setLevel(Level.A);
RunnerStatus runnerStatus = new RunnerStatus();
runnerStatus.setRunningStatus(RunningStatus.D);
runnerInfo.setStatus(runnerStatus);
runner.setInfo(runnerInfo);
Race race = new Race();
race.setName("The Ultimate Marathon");
race.addRunner(runner);
Organizer organizer = new Organizer();
organizer.setName("Joe Organ");
organizer.setRace(race);
Responsibility responsibility = new Responsibility();
responsibility.setUniqueIdentifier(System.currentTimeMillis());
responsibility.setDescription("Raise funds");
race.addOrganizer(organizer, responsibility);
em.persist(race);
em.persist(organizer);
em.persist(runner);
em.flush();
// Clear the cache
em.clear();
clearCache();
Runner runnerRefreshed = em.find(Runner.class, runner.getId());
assertTrue("The age conversion did not work.", runnerRefreshed.getAge() == 52);
assertTrue("The embeddable health conversion did not work.", runnerRefreshed.getInfo().getHealth().equals(Health.HEALTHY));
assertTrue("The embeddable level conversion did not work.", runnerRefreshed.getInfo().getLevel().equals(Level.AMATEUR));
assertTrue("The nested embeddable running status conversion did not work.", runnerRefreshed.getInfo().getStatus().getRunningStatus().equals(RunningStatus.DOWN_TIME));
assertTrue("The number of personal bests for this runner is incorrect.", runnerRefreshed.getPersonalBests().size() == 2);
assertTrue("Distance (map key) conversion did not work.", runnerRefreshed.getPersonalBests().keySet().contains("10K"));
assertTrue("Distance (map key) conversion did not work.", runnerRefreshed.getPersonalBests().keySet().contains("5K"));
assertTrue("Time (map value) conversion did not work.", runnerRefreshed.getPersonalBests().values().contains("47:34.0"));
assertTrue("Time (map value) conversion did not work.", runnerRefreshed.getPersonalBests().values().contains("26:41.0"));
Race raceRefreshed = em.find(Race.class, race.getId());
Map<Responsibility, Organizer> organizers = raceRefreshed.getOrganizers();
assertFalse("No race organizers returned.", organizers.isEmpty());
assertTrue("More than one race organizer returned.", organizers.size() == 1);
Responsibility resp = organizers.keySet().iterator().next();
assertTrue("Responsibility was not uppercased by the converter", resp.getDescription().equals("RAISE FUNDS"));
for (String accomplishment : runnerRefreshed.getAccomplishments().keySet()) {
assertTrue("Accomplishment (map key) conversion did not work.", accomplishment.endsWith("!!!"));
}
} finally {
closeEntityManagerAndTransaction(em);
}
}
/**
* Test that application exceptions thrown from the converter's convertToEntityAttribute method get
* wrapped in a PersistenceException.
* Added for bug 402029: Application exceptions need to be wrapped in PersistenceException
*/
public void testConverterExceptionWrapping1() {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
// setup
Race race = new Race();
race.setName("Just Another Marathon");
Organizer organizer = new Organizer();
organizer.setName("John Smith");
organizer.setRace(race);
Responsibility responsibility = new Responsibility();
responsibility.setUniqueIdentifier(System.currentTimeMillis());
//This string causes an exception to be thrown from the ResponsibilityConverter.convertToEntityAttribute method
responsibility.setDescription(ResponsibilityConverter.THROW_EXCEPTION_IN_TO_ENTITY_ATTRIBUTE);
race.addOrganizer(organizer, responsibility);
try {
em.persist(race);
em.persist(organizer);
em.flush();
// Clear the cache
em.clear();
clearCache();
race = em.find(Race.class, race.getId());
//trigger indirection
race.getOrganizers().get(responsibility);
} catch (jakarta.persistence.PersistenceException pe) {
if (pe.getCause() == null ||
!ResponsibilityConverter.THROW_EXCEPTION_IN_TO_ENTITY_ATTRIBUTE.equals(pe.getCause().getMessage())) {
//rethrow this exception because it does not contain the expected application generated RuntimeException
throw pe;
}//else this error is expected and can be ignored
} catch (RuntimeException unexpected) {
if (ResponsibilityConverter.THROW_EXCEPTION_IN_TO_ENTITY_ATTRIBUTE.equals(unexpected.getMessage())) {
fail("Application exception thrown from convertToEntityAttribute was not wrapped in a PersistenceException");
}
throw unexpected;
}
} finally {
closeEntityManagerAndTransaction(em);
}
}
/**
* Test that application exceptions thrown from the converter's convertToDatabaseColumn method get
* wrapped in a PersistenceException.
* Added for bug 402029: Application exceptions need to be wrapped in PersistenceException
*/
public void testConverterExceptionWrapping2() {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
// setup
Race race = new Race();
race.setName("Just Another Marathon");
Organizer organizer = new Organizer();
organizer.setName("John Smith");
organizer.setRace(race);
Responsibility responsibility = new Responsibility();
responsibility.setUniqueIdentifier(System.currentTimeMillis());
//This string causes an exception to be thrown from the ResponsibilityConverter.convertToDatabaseColumn method
responsibility.setDescription(ResponsibilityConverter.THROW_EXCEPTION_IN_TO_DATABASE_COLUMN);
race.addOrganizer(organizer, responsibility);
try {
em.persist(race);
em.persist(organizer);
em.flush();
} catch (jakarta.persistence.PersistenceException pe) {
if (pe.getCause() == null ||
!ResponsibilityConverter.THROW_EXCEPTION_IN_TO_DATABASE_COLUMN.equals(pe.getCause().getMessage())) {
//rethrow this exception because it does not contain the expected application generated RuntimeException
throw pe;
}//else this error is expected and can be ignored
} catch (RuntimeException unexpected) {
if (ResponsibilityConverter.THROW_EXCEPTION_IN_TO_DATABASE_COLUMN.equals(unexpected.getMessage())) {
fail("Application exception thrown from convertToDatabaseColumn was not wrapped in a PersistenceException");
}
throw unexpected;
}
} finally {
closeEntityManagerAndTransaction(em);
}
}
/**
* Verify {@code @Convert} annotation on {@code @ElementCollection} mapping. Two collections are defined, first
* with annotation and second one without it. Tests verifies if converter class is applied on both collections
* or not.
*/
public void testConvertOnElementCollection() {
final EntityManager em = createEntityManager();
try {
beginTransaction(em);
// Converter used in @ElementCollection mapping.
final CompetitionConverter converter = new CompetitionConverter();
// Run test for all existing Runner instances.
final TypedQuery<Runner> rq = em.createNamedQuery("Runner.listAll", Runner.class);
final List<Runner> runners = rq.getResultList();
assertTrue("No Runner object was returned", runners.size() > 0);
for (Runner runner : runners) {
// Verify Map processed with @Convert annotation.
final Map <String, RunnerVictory> victoriesThisYear = runner.getVictoriesThisYear();
final Query rvtq = em.createNamedQuery("RunnerVictoryThis.getById");
rvtq.setParameter(1, runner.getId());
final List<Object[]> victories = rvtq.getResultList();
assertEquals("Count of retrieved objects from database shall be the same.",
runner.getVictoriesThisYear().size(), victories.size());
for (Object[] victory : victories) {
final String name = (String)victory[0];
final Long id = ((Number)victory[1]).longValue();
final String competition = (String)victory[2];
final RunnerVictory entityVictory = victoriesThisYear.get(name);
String entityCompetitionConverted
= converter.convertToDatabaseColumn(entityVictory.getCompetition());
String dbCompetitionConverted = converter.convertToEntityAttribute(competition);
assertFalse("Entity and database values shall not match.",
competition.equals(entityVictory.getCompetition()));
assertTrue("Manually converted entity value and plain database value shall match.",
competition.equals(entityCompetitionConverted));
assertTrue("Manually converted database value and plain entity value shall match.",
entityVictory.getCompetition().equals(dbCompetitionConverted));
}
}
for (Runner runner : runners) {
// Verify Map not processed with @Convert annotation.
final Map <String, RunnerVictory> victoriesLastYear = runner.getVictoriesLastYear();
final Query rvlq = em.createNamedQuery("RunnerVictoryLast.getById");
rvlq.setParameter(1, runner.getId());
final List<Object[]> victories = rvlq.getResultList();
assertEquals("Count of retrieved objects from database shall be the same.",
runner.getVictoriesLastYear().size(), victories.size());
for (Object[] victory : victories) {
final String name = (String)victory[0];
final Long id = ((Number)victory[1]).longValue();
final String competition = (String)victory[2];
final RunnerVictory entityVictory = victoriesLastYear.get(name);
// Converter is missing so only entity to database conversion check makes sense.
String entityCompetitionConverted
= converter.convertToDatabaseColumn(entityVictory.getCompetition());
assertTrue("Entity and database values shall match.",
competition.equals(entityVictory.getCompetition()));
assertFalse("Manually converted entity value and plain database value shall not match.",
competition.equals(entityCompetitionConverted));
}
}
} finally {
closeEntityManagerAndTransaction(em);
}
}
@Override
public String getPersistenceUnitName() {
return "MulitPU-1";
}
}