How to use MySQL-specific functions in H2 database? - spring

How can I persist an entity that contains a #Formula field in it to H2 database?
I encounter the following exception:
...
Caused by: org.h2.jdbc.JdbcSQLException: Function "UNIX_TIMESTAMP" not found;
...
Post class:
#Entity
public class Post {
// ...
#Formula("UNIX_TIMESTAMP(creation_date_time) / 24 * 60 * 60 * 1000 + likes_count")
private long score;
// ...
}
PostRepositoryTest class:
#ExtendWith(SpringExtension.class)
#DataJpaTest
class PostRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private PostRepository postRepository;
#Test
void savePost() {
entityManager.persist(new Post());
List<Post> posts = postRepository.findAll();
assertEquals(1, posts.size());
}
}

According to https://h2database.com/html/features.html#user_defined_functions, you could define such a function in src/test/resources/import.sql:
CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "acme.H2Function.unixTimestamp";
and define the this class:
package acme;
public class H2Function {
public static long unixTimestamp(java.sql.Timestamp timestamp) {
return timestamp.getTime() / 1000L;
}
}
Spring JPA will auto call import.sql when you start you unit test.
NOTE: Must naming import.sql, not schema.sql, not data.sql

Related

SpringBootTest in error with h2 v2.1.214 when multiple schema

I have some tests in error after upgrading from Spring boot 2.5.6 to 2.7.3. With this upgrade, h2 version has been upgraded from 1.4.200 to 2.1.214. In the pom, the version of spring-test-dbunit used is 1.3.0. The version of dbunit is 2.7.3.
For information we use Oracle for the database and h2 for tests.
Our application works on two different schema.
I have some entities like this:
#Entity
#Table(name = "TABLEONE")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
public class TableOne implements Serializable {
...
}
#Entity
#Table(schema = "OTHERSCHEMA", name = "TABLETWO_C")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TableTwo {
...
}
#Entity
#Table(schema = "OTHERSCHEMA", name = "TABLETHREE")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
public class TableThree {
...
#ManyToOne
#JoinColumn(name = "ID_TABLEONE")
private TableOne tableOne;
....
}
In the application.yml for tests, we have this (for "spring.datasource.url"):
url: jdbc:h2:mem:my-app;DB_CLOSE_DELAY=-1;MODE=Oracle
For the tests, we have one configuration for dbunit like this:
#TestConfiguration
public class DbUnitTestConfiguration {
#Autowired
private DataSource dataSource;
public DatabaseConfigBean getDatabaseConfigBean(boolean qualifiedTableNames) {
final DatabaseConfigBean databaseConfigBean = new DatabaseConfigBean();
databaseConfigBean.setDatatypeFactory(new H2DataTypeFactory());
databaseConfigBean.setQualifiedTableNames(qualifiedTableNames);
return databaseConfigBean;
}
#Bean(name = "dbUnitDatabaseConnection")
public DatabaseDataSourceConnectionFactoryBean getDatabaseDataSourceConnectionFactoryBean() {
final DatabaseDataSourceConnectionFactoryBean databaseConfigBean = new DatabaseDataSourceConnectionFactoryBean();
databaseConfigBean.setDatabaseConfig(getDatabaseConfigBean(false));
databaseConfigBean.setDataSource(dataSource);
return databaseConfigBean;
}
#Bean(name = "otherSchemaDatabaseConnection")
public DatabaseDataSourceConnectionFactoryBean getOtherSchemaDatabaseDataSourceConnectionFactoryBean() {
final DatabaseDataSourceConnectionFactoryBean databaseConfigBean = new DatabaseDataSourceConnectionFactoryBean();
databaseConfigBean.setDatabaseConfig(getDatabaseConfigBean(true));
databaseConfigBean.setDataSource(dataSource);
databaseConfigBean.setSchema("OTHERSCHEMA");
return databaseConfigBean;
}
}
The tests are like this:
#ExtendWith(SpringExtension.class)
#SpringBootTest(properties = { "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" })
#AutoConfigureMockMvc
#ContextConfiguration(classes = { SecurityTestConfiguration.class, SpecificWebMvcTestConfiguration.class,
DbUnitTestConfiguration.class })
#TestExecutionListeners(listeners = DbUnitTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
#DbUnitConfiguration(databaseConnection = { "dbUnitDatabaseConnection", "otherSchemaDatabaseConnection" })
#WithMockUser
#DatabaseSetup(connection = "otherSchemaDatabaseConnection", value = { "classpath:dataSet/tableTwo.xml" })
#DatabaseSetup(value = { "classpath:dataSet/tableOne.xml" })
#DatabaseTearDown(value = { "classpath:dataSet/tableOne.xml" }, type = DatabaseOperation.DELETE_ALL)
#DatabaseTearDown(connection = "otherSchemaDatabaseConnection", value = {
"classpath:dataSet/tableTwo.xml" }, type = DatabaseOperation.DELETE_ALL)
public class MyTest {
#Autowired
private MockMvc mvc;
#Test
public void myTest() throws Exception {
...
}
}
I have errors like this:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "TABLEONE" not found; SQL statement:
alter table otherschema.tablethree add constraint FKexerkn1ptnbr1k56036qd0snn foreign key (id_tableone) references tableone [42102-214]
and
org.dbunit.dataset.NoSuchTableException: OTHERSCHEMA.TABLETWO_C
I also see that log:
org.dbunit.database.DatabaseDataSet : Table 'OTHERSCHEMA.TABLETWO_C' not found in tableMap=org.dbunit.dataset.OrderedTableNameMap[_tableNames=[OTHERSCHEMA.TABLETEN, OTHERSCHEMA.TABLEELEVEN...], _tableMap={OTHERSCHEMA.TABLETEN=null, OTHERSCHEMA.TABLEELEVEN=null...}, _caseSensitiveTableNames=false]
It seems that the configuration that was done before and working with previous version of h2 does not work with new version of h2. I saw a solution by setting qualified table names to true and it is what is done for the other schema so I do not understand.
For the first error, the alter table works on a table of otherschema and did not find tableone that is in the default schema (dbUnitDatabaseConnection connection) so it seems to be a problem between dbUnitDatabaseConnection
and otherSchemaDatabaseConnection no?
For the second error, I do not understand why the table does not exist in the list of tables with OTHERSCHEMA indicated in the log.
Can you help me please?

Spring TransactionManager behavior with Spring Data and JpaRepository

I have a controller which does the following
A submit end point which save an entry in db and then call some external service asynchronously
Track the update of asynchronous call (this call updates an associated table) by watching the db and update the status of the entry created in step one
I was using the #Query Annotation to verify if step one entry exist in db and it was always returning empty. I tried changing it to the default spring method and it starts returning the inserted value.
I read about proxies, #Transactional and how non CRUD methods in a JPARepository are non transactional and tried few things like transaction propagation and self injection and even explicitly marking the repo method #Transactional. But none of them fixed the issue. Using spring data method solved it but I still don't understand what happened. Can someone help with an explanation of this behavior.
Basic code snippet is below
MyController
#RestController
public class MyController {
private final MyService myService;
private final MyRepository myRepository;
#Autowired
public MyController(MyService myService,
MyRepository myRepository) {
this.myService = myService;
this.myRepository = myRepository;
}
#PostMapping(value = "/submit")
public ResponseEntity<MyResponse> submit(#Valid #RequestBody MyRequest myRequest) {
return ResponseEntity
.accepted()
.body(MyResponse.success(myService.submit(myRequest), "SUBMITTED"));
}
/**
* This method is to update the status of the entry created by /submit endpoint
* if the asynchoronous process triggered by submit endpoint update an associated table
*/
#PostConstruct
private void trackUpdates() {
..
someObserver.subscribe(trackedAssociatedEntity -> {
myService.trackAndUpdateBasedOnAssociatedEntity(trackedAssociatedEntity);
});
}
}
MyService
#Service
#Transactional
public class MyService {
private final MyRepository myRepository;
#Autowired
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
submit(MyRequest myRequest) {
myRepository.save(myEntity);
//makes that asynchronous call
}
public void trackAndUpdateBasedOnAssociatedEntity(#NotNull MyAssociatedEntity myassociatedEntity) {
// This commented call always return empty but the uncommented code works as expected
// List<MyEntity> existingEntity =
// myRepository.findEntityByField1AndField2(myassociatedEntity.getField1(),myassociatedEntity.getField2());
List<MyEntity> existingEntities =
myRepository.findByField1AndField2(myassociatedEntity.getField1(),myassociatedEntity.getField2());
if(existingEntities.isEmpty()){
//create new
}else{
//update
}
}
}
}
}
MyRepository
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT e FROM MyEntity e WHERE e.field1 = ':field1' and e.field2 = ':field2' ")
List<MyEntity> findEntityByField1AndField2(String field1, String field2);
List<MyEntity> findByField1AndField2(String field1, String field2);
}
I believe that '' are not needed. Please try the following:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT e FROM MyEntity e WHERE e.field1 = :field1 and e.field2 = :field2")
List<MyEntity> findEntityByField1AndField2(String field1, String field2);
List<MyEntity> findByField1AndField2(String field1, String field2);
}

Spring Data REST Custom Resource URI works for String but not Long

I have a model:
public class MyModel {
#Id private Long id;
private Long externalId;
// Getters, setters
}
I'd like to use externalId as my resource identifier:
#Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {
configuration
.withEntityLookup()
.forRepository(MyRepository.class, MyModel::getExternalId, MyRepository::findByExternalId);
}
}
If externalId is a String, this works fine. But since it's a number (Long)
public interface MyRepository extends JpaRepository<MyModel, Long> {
Optional<MyModel> findByExternalId(#Param("externalId") Long externalId);
}
when invoking: /myModels/1 I get:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at org.springframework.data.rest.core.config.EntityLookupConfiguration$RepositoriesEntityLookup.lookupEntity(EntityLookupConfiguration.java:213) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeFindOne(UnwrappingRepositoryInvokerFactory.java:130) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:524) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:335) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
...
A separate custom EntityLookupSupport<MyModel> component class works.
Am I missing something to get it working for Long using method references in my RepositoryRestConfigurerAdapter?
Try to add this to your RepositoryEntityLookupConfig class:
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(String.class, Long.class, Long::parseLong);
super.configureConversionService(conversionService);
}
Do you really need to set configuration by yourself ? You could try to use spring-boot auto-configuration by adding #RepositoryRestResource annotation
#RepositoryRestResource(collectionResourceRel = "myModels", path = "myModels")
public interface MyRepository extends JpaRepository<MyModel, Long> {
Optional<MyModel> findByExternalId(#Param("externalId") Long externalId);
}
Also add #Entity on your model class
#Entity
public class MyModel {
#Id
private Long id;
#Column(name = "EXTERNAL_ID")
// Column annotation is not required if you respect case-sensitive
private Long externalId;
// Getters, setters
}
Apparently, the default BackendIdConverter (see DefaultIdConverter) does nothing with ID conversion and on the other hand Spring Data Rest cannot use the repository ID type. So, you have to either convert it yourself or configure your custom ID converter bean, for example:
#Bean
public BackendIdConverter myModelBackendIdConverter() {
return new BackendIdConverter() {
#Override
public Serializable fromRequestId(final String id, final Class<?> entityType) {
return Optional.ofNullable(id).map(Long::parseLong).orElse(null);
}
#Override
public boolean supports(final Class<?> delimiter) {
return MyModel.class.isAssignableFrom(delimiter);
}
#Override
public String toRequestId(final Serializable id, final Class<?> entityType) {
return Optional.ofNullable(id).map(Object::toString).orElse(null);
}
};
}
See also:
BackendIdHandlerMethodArgumentResolver
#BackendId
The signature of the method you are trying to call seems to be:
forRepository(Class<R> type, Converter<T,ID> identifierMapping,
EntityLookupRegistrar.LookupRegistrar.Lookup<R,ID> lookup)
I don't see how MyModel::getExternalId can be doing the necessary conversion.
I would try something like the following:
#Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {
configuration
.withEntityLookup()
.forRepository(MyRepository.class, Long::parseLong, MyRepository::findByExternalId);
}
}

Spring boot application with EnableCaching How to retrieve guava cache content

It'm using a spring boot application with cache enabled.
Environment (pom.xml):
Spring:
org.springframework.boot:spring-boot-starter-amqp:jar:1.3.3.RELEASE
org.springframework:spring-messaging:jar:4.2.5.RELEASE
org.springframework.amqp:spring-rabbit:jar:1.5.4.RELEASE
org.springframework.retry:spring-retry:jar:1.1.2.RELEASE
org.springframework:spring-core:jar:4.2.5.RELEASE:compile
org.springframework.cloud:spring-cloud-aws-context:jar:1.0.4.RELEASE
org.springframework:spring-context:jar:4.2.5.RELEASE
org.springframework.data:spring-data-jpa:jar:1.9.4.RELEASE
org.springframework:spring-context-support:jar:4.2.5.RELEASE
Hibernate
org.hibernate:hibernate-validator:jar:5.2.2.Final
org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final
com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:jar:2.6.5
org.hibernate:hibernate-entitymanager:jar:5.1.0.Final
org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final
org.hibernate:hibernate-java8:jar:5.1.0.Final
org.hibernate:hibernate-envers:jar:5.1.0.Final
Configuration Cache (on Spring boot application):
#Configuration
#EnableCaching
public class ApplicationCacheConfig extends CachingConfigurerSupport {
/**
* Configuration Table Cache
*/
public static final String CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME = "CONFIGURATION_TABLE_FIND_BY_ID_CACHE";
public static final String CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME = "CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE";
#Bean
#Override
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
Collection<Cache> caches = Lists.newArrayList();
caches.addAll(buildConfigurationCache());
simpleCacheManager.setCaches(caches);
return simpleCacheManager;
}
private Collection<Cache> buildConfigurationCache() {
List<Cache> caches = Lists.newArrayList();
// This cache never expires and don't have a maximum size because the table Configuration is not transactional
GuavaCache cacheFindById = new GuavaCache(CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME,
CacheBuilder.newBuilder().build());
caches.add(cacheFindById);
// This cache never expires and don't have a maximum size because the table Configuration is not transactional
GuavaCache cacheFindByService = new GuavaCache(CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME,
CacheBuilder.newBuilder().build());
caches.add(cacheFindByService);
return caches;
}
}
Hibernate entity:
#Entity
#Table(name = Configuration.TABLE_NAME)
#DynamicUpdate
public class Configuration implements Serializable {
public static final String TABLE_NAME = "configuration";
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#Convert(converter = ConfigurationConverter.class)
private ConfigurationEnum id;
#Column(name = "service", nullable = false)
#NotNull
#Convert(converter = ServiceConverter.class)
private ServiceEnum service;
}
Repository (Spring-data):
public interface ConfigurationRepository extends PagingAndSortingRepository<Configuration, Integer>,
JpaSpecificationExecutor<Configuration> {
#Cacheable(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME)
Configuration findById(ConfigurationEnum configurationEnum);
#Cacheable(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME)
List<Configuration> findByService(ServiceEnum service);
}
Configuration Enum:
#Getter
#AllArgsConstructor
public enum ConfigurationEnum {
CONFIG_1(1),
CONFIG_2(2);
private int id;
}
Configuration Converter:
#Converter
public class ConfigurationConverter implements AttributeConverter<ConfigurationEnum, Integer> {
#Override
public Integer convertToDatabaseColumn(ConfigurationEnum key) {
return key == null ? null : (int) key.getId();
}
#Override
public ConfigurationEnum convertToEntityAttribute(Integer key) {
return key == null ? null : Stream.of(ConfigurationEnum.values())
.filter(step -> key.equals(step.getId()))
.findFirst()
.orElse(null);
}
}
Test IT:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ApplicationIT.class)
#WebAppConfiguration
#Transactional
public class ConfigurationCacheIT {
#Autowired
ConfigurationRepository configurationRepository;
#Autowired
protected CacheManager cacheManager;
#Test
public void configuration_findById_cache_success() {
Configuration config = configurationRepository.findById(ConfigurationEnum.CONFIG_1);
// An ORM request is performed - CHECK
Assert.assertNotNull(step); // TEST OK
Cache.ValueWrapper entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.getId());
Assert.assertNull(entry); OK
config = configurationRepository.findById(ConfigurationEnum.CONFIG_1);
// No ORM request is performed - CHECK
Assert.assertNotNull(step); // TEST OK
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.getId());
Assert.assertNotNull(entry); **// TEST FAIL !!!**
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.name());
Assert.assertNotNull(entry); **// TEST FAIL !!!**
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1);
Assert.assertNotNull(entry); **// TEST FAIL !!!**
}
protected Cache.ValueWrapper getCacheEntry(String cacheName, Object key) {
return cacheManager.getCache(cacheName).get(key);
}
#Test
public void configuration_findByAll_without_cache_success() {
ArrayList<Configuration> list1 = Lists.newArrayList(configurationRepository.findAll());
// An ORM request is executed
Assert.assertNotNull(list1);
Assert.assertEquals(ConfigurationEnum.values().length, list1.size());
ArrayList<Configuration> list2 = Lists.newArrayList(configurationRepository.findAll());
// Another ORM request is executed
Assert.assertNotNull(list2);
Assert.assertEquals(ConfigurationEnum.values().length, list2.size());
}
}
My question is why my tests are failing?
Actually this was a non issue.
I'm using the fallowing architecture:
App-mdw (Middleware layer) (Spring boot App with #EnableCaching annotation)
App-ws (WebServices layer) (Spring boot App without #EnableCaching annotation)
The above tests were executed on the application App-ws and the annotation is not inherited that's why the caching was not working.
The right assert was:
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1);
Assert.assertNotNull(entry)

Update Logic for JPA Entity Fails when using DBUnit and Spring

I am currently using DBUnit in conjunction with Spring in order to unit test my application, but have run into an issue where my update logic test always fails because a deadlock occurs on the database and I cannot figure out why this is the case. Please note that I have been able to get around the issue by removing the method annotated by #After, which really isn't needed because I am using the #TransactionConfiguration annotation, but I'm concerned that I'm misunderstanding something regarding how the transaction processing works and thus am hoping someone can indicate why I always get the following exception when running my updateTerritory method.
java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
One thing that may be helpful to point out is that I am able to perform other actions like querying the database and inserting new records without any lock errors. In addition I am using OpenJPA and spring is injecting the PersistenceUnit into my DAO. I'm guessing that mixing up the usage of the PersistenceUnit and the direct use of the datasource within my DBUnit setup code (testSetup and testTeardown) may be part of the issue. I'm currently using Derby as my database.
My Code is provided below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/applicationContext.xml")
#TransactionConfiguration(defaultRollback = true)
public class TerritoryZoneManagerTest {
#Autowired
private DataSource unitTestingDataSource;
#Autowired
private ITerritoryZoneDaoManager mgr;
#Before
public void testSetup() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#After
public void testTeardown() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.DELETE.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#Test
#Transactional
public void updateTerritory() {
TerritoryZone zone = new TerritoryZone();
int id = 1;
zone = mgr.getTerritory(id);
String newCity = "Congerville";
zone.setCity(newCity);
mgr.updateTerritory(zone);
zone = mgr.getTerritory(id);
Assert.assertEquals(newCity, zone.getCity());
}
}
The DAO object is provided below as well in case that is useful.
#Repository
public class TerritoryZoneDaoManager implements ITerritoryZoneDaoManager {
/*
#Autowired
private EntityManagerFactory emf;
*/
/*
* #PersistenceUnit EntityManagerFactory emf;
*
* #PersistenceContext private EntityManager getEntityManager(){ return
* emf.createEntityManager(); }
*/
#PersistenceContext
private EntityManager em;
private EntityManager getEntityManager() {
// return emf.createEntityManager();
return em;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#addTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone addTerritory(TerritoryZone territoryZone) {
EntityManager em = getEntityManager();
em.persist(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getTerritory(int)
*/
#Override
public TerritoryZone getTerritory(int id) {
TerritoryZone obj = null;
Query query = getEntityManager().createNamedQuery("selectById");
query.setParameter("id", id);
obj = (TerritoryZone) query.getSingleResult();
return obj;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#updateTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone updateTerritory(TerritoryZone territoryZone){
getEntityManager().merge(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getActiveTerritoriesByStateZipLob(java.lang.String, java.lang.String, java.util.Date, java.lang.String)
*/
#Override
public List<TerritoryZone> getActiveTerritoriesByStateZipLob(String stateCd, String zipCode, Date effectiveDate, String lobCd){
List<TerritoryZone> territoryList;
Query query = getEntityManager().createNamedQuery("selectActiveByZipStateLob");
query.setParameter("zipCode", zipCode);
query.setParameter("state", stateCd);
query.setParameter("lob",lobCd);
query.setParameter("effectiveDate", effectiveDate);
territoryList = (List<TerritoryZone>) query.getResultList();
return territoryList;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#deleteAll()
*/
#Override
public void deleteAll(){
Query query = getEntityManager().createNativeQuery("Delete from TerritoryZone");
query.executeUpdate();
}
/***
* the load method will remove all existing records from the database and then will reload it using it the data passed.
* #param terrList
*/
public void load(List<TerritoryZone> terrList){
deleteAll();
for (TerritoryZone terr:terrList){
addTerritory(terr);
}
}
}
Thanks in advance for your assistance.
Jeremy
jwmajors81
i can not know what's wrong with your unit testing code for lacking some details.
i also used spring unit test and dbunit for my himvc framework, a RAD framework based on spring3 and hibernate. here is the code of my super class for unit testingļ¼Œ
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/application-test-config.xml"})
#Transactional
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class HiMVCTransactionalUnitTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
protected DBUnitHelper dbHelper;
protected void loadFixture(){
try{
String fixtureFile=this.dbHelper.getDataFile();
if(fixtureFile==null){
fixtureFile=this.getDefaultXMLFixtureFile();
this.dbHelper.setDataFile(fixtureFile);
}
if(this.dbHelper.isDataFileExisted()){
if(this.dbHelper.isMSSQL()){
HiMVCInsertIdentityOperation operation=new HiMVCInsertIdentityOperation(DatabaseOperation.CLEAN_INSERT);
operation.setInTransaction(true);
this.dbHelper.executeDBOperation(operation);
}else{
this.dbHelper.executeDBOperation(DatabaseOperation.CLEAN_INSERT);
}
}
}catch(Exception x){
x.printStackTrace();
}
}
...
}
i use the #Transactional annotation in the class declaration, and also specified the transactionManager. i wrote a DBUnitHelper to wrap the dbunit details of data loading.
here is a unit test sample:
public class MyTest extends HiMVCTransactionalUnitTest{
#Before
public void setup(){
super.loadFixture();
}
//other testing methods
}
Hope these codes helpful.

Resources