Spring Batch read DB2 write Mysql duplicated records in Resultset - mysql

I'm trying to read from DB2 with a stored procedure ( SP ) that returns records based on page size and start page among other input parameters. My goal is however to request all the records in one call and load another table in Mysql using Spring Batch. Seems straight forward enough, however when I run the job below it returns the correct number of records as per the SP but the records are skewed and duplicated as if the transactions are not working properly. The reader uses one #Primary Datasources ( DS ), for the DB2 read and another DS for the JpaItemWriter destination (LocalContainerEntityManagerFactoryBean).
My understanding is that DB2 and Mysql returns a ResultSet so don't know why I am forced to add these lines to avoid Invalid Parameter Type or Cursor position errors....
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
I am reading from DB2 then inserting into a MySql table based on the supplied AccountCashRowMapper. It's a strange situation that almost works, the listener doesn't report any errors.
Spring Batch is supposed to inherently manage the transactions for me, however what configuration or code is missing to make this work ?
#EnableBatchProcessing
public class LoadAccountCashTableJob {
private static final Logger logger = LoggerFactory.getLogger(LoadAccountCashTableJob.class);
#Autowired
ApplicationContext context;
#Autowired
public AccountCashRepository repo;
#Autowired
public EntityManager em;
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private AccountCashProcessor accountCashProcessor;
#Autowired
private TaskExecutor taskExecutor;
#Autowired
#Qualifier("originDataSource")
protected DataSource dataSource;
#Qualifier(value = "AccountCashJob")
#Bean
public Job AccountCashJob() throws Exception{
return this.jobBuilderFactory.get("AccountCashJob")
.start(this.accountCashStep())
.build();
}
#Bean
public Step accountCashStep() throws Exception{
return this.stepBuilderFactory.get("accountCashStep")
//.transactionManager(transactionManager)
.<String, String>chunk(500)
.reader(this.cashSPReader(dataSource))
.processor(accountCashProcessor)
.writer(this.accountCashItemWriter() )
.taskExecutor(taskExecutor)
.listener(new ItemFailureLoggerListener())
.build();
}
#Bean(destroyMethod="")
#StepScope
public StoredProcedureItemReader cashSPReader (DataSource dataSource) throws Exception {
StoredProcedureItemReader reader = new StoredProcedureItemReader();
SqlParameter[] parameters = new SqlParameter[7];
parameters[0] = new SqlParameter("DIV", Types.VARCHAR);
parameters[1] = new SqlParameter("PageDebut", Types.SMALLINT);
parameters[2] = new SqlParameter("NbParPage",Types.SMALLINT);
parameters[3] = new SqlInOutParameter("O_NB_PAGES",Types.SMALLINT);
parameters[4] = new SqlInOutParameter("O_RC",Types.INTEGER);
parameters[5] = new SqlInOutParameter("O_RC_DSC",Types.VARCHAR);
parameters[6] = new SqlReturnResultSet(ACCOUNTCASH_RESULT_SET, new AccountCashRowMapper());
reader.setDataSource(dataSource);
reader.setProcedureName("VMDTSTSP.db2cdb_List_Positions_Encaisse");
reader.setRowMapper(new AccountCashRowMapper()); //FOR the output records..
reader.setParameters(parameters);
reader.setPreparedStatementSetter(new SPParamSetter());
reader.afterPropertiesSet();
logger.info("reader.getSql()>>>>>>>>>>>>>>"+reader.getSql());
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
return reader;
}
#Bean(destroyMethod="")
#StepScope
public JpaItemWriter<AccountCash> accountCashItemWriter() {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean("entityManagerFactory");
em.createNativeQuery("TRUNCATE TABLE purefacts.account_cash").executeUpdate();
JpaItemWriter<AccountCash> accountCashJpaItemWriter = new JpaItemWriter<>();
accountCashJpaItemWriter.setEntityManagerFactory(emf);
accountCashJpaItemWriter.setUsePersist(true);
return accountCashJpaItemWriter;
}
public static class SPParamSetter implements PreparedStatementSetter {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
AS400JDBCCallableStatement eventCallableSt=(AS400JDBCCallableStatement)ps;
eventCallableSt.setString(1, ACCOUNTCASH_DIV);
eventCallableSt.setInt(2, ACCOUNTCASH_START_PAGE);
eventCallableSt.setInt(3,ACCOUNTCASH_PAGE_SIZE);
eventCallableSt.registerOutParameter(4, Types.SMALLINT);
eventCallableSt.setInt(4, ACCOUNTCASH_O_NB_PAGES);
eventCallableSt.registerOutParameter(5, Types.INTEGER);
eventCallableSt.setInt(5, ACCOUNTCASH_O_ERROR_CODE);
eventCallableSt.registerOutParameter(6, Types.VARCHAR);
eventCallableSt.setString(6, ACCOUNTCASH_O_ERROR_DESCRIPTION);
eventCallableSt.getResultSet();
}
}
public class ItemFailureLoggerListener extends ItemListenerSupport {
private Log logger = LogFactory.getLog("item.error");
public void onReadError(Exception e) {
logger.error("Encountered error on read", e);
}
public void onWriteError(Exception ex, List items) {
logger.error("Encountered error on write", ex);
}
}
}

Related

Intgration Test in springboot pass when test at each method but fail when running class

It seems pass when a run the test individually but fine when fair when running the class .I have tried to use #Before and #After annotatoon.And only the Dirtiescontext most fit my case.My question is any alternative for dirtiescontext(too slow) or any method can suit my case?
My test code:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#AutoConfigureMockMvc
class UserCoreControllerTest {
#Autowired
private UserRepository userRepository;
#Autowired
private UserCoreService userCoreService;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private MockMvc mockMvc;
/*
*//* #BeforeTestMethod
public void setUp(){
System.out.println("begin");
userRepository.save(mockUser());
}
#AfterTestMethod
public void tearOff(){
System.out.println("end");
userRepository.deleteById(1);
}
*/
private User mockUser(){
User user= new User(0,"admin","admin",null,null,"yl","sd"
,"434","dsf",null,4,2,new ArrayList<>());
user.getApplications().add(mockJobOrder("job1title","b"));
user.getApplications().add(mockJobOrder("job2title","d"));
return user;
}
private JobOrder mockJobOrder(String title,String des){
return new JobOrder(0,1,title,des,null,null,0,2,false,0,null);
}
#Test
void getProfile() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/getProfile/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.username")
.value("admin"))
.andDo(print());
}
#Test
void getUserByName() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/get/Byusername/{username}", "admin")
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.password").value("admin"));
//if cant found user
MvcResult whatever = mockMvc.perform(get("/UserJob/get/Byusername/{username}", "whatever")
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andReturn();
Assertions.assertEquals("",whatever.getResponse().getContentAsString());
}
#Test
void updateUser() throws Exception {
userRepository.save(mockUser());
User updated=new User(1,"alex","admin",null,null,"yl","sd"
,"434","dsf",null,4,2,null);
String request=objectMapper.writeValueAsString(updated);
MvcResult mvcResult=mockMvc.perform(MockMvcRequestBuilders.put("/UserJob/updateuser")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON_VALUE)
.content(request)
)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
Assertions.assertEquals("Successful update",mvcResult.getResponse().getContentAsString());
mockMvc.perform(get("/UserJob/getProfile/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.username")
.value("alex"))
.andDo(print());
}
#Test
void showApplicationshistory() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/application/history/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title").value("job1title"))
.andExpect(jsonPath("$[1].title").value("job2title"))
.andDo(print());
}
#Test
void addUser() throws Exception {
User user=mockUser();
user.setUsername("tom");
String request=objectMapper.writeValueAsString(user);
mockMvc.perform(MockMvcRequestBuilders.post("/UserJob/add/user")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
.accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("tom"));
}
Application properties(i test it on a real database with drop-create)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=*
spring.datasource.password=*
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.defer-datasource-initialization=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.sql.init.mode=always
logging.level.root=INFO
I guess that the test are failing because of some user constraints - since you insert the user a multiple times here.
There are multiple solutions for that.
Mark your test as transactional. The transaction is automatically rollbacked for tests.
Clear the tables manually after/before each test. Either by using the repository.deleteAll() or truncate the whole db with some sql statements.
Just as a side information: You can also use testcontainers instead of having a persistente database locally. See https://www.testcontainers.org/ or even better: a wrapper library around it: https://github.com/PlaytikaOSS/testcontainers-spring-boot

Spring #Transactional does not rollback checked exceptions

I have been stuck for a while to make Spring rollback a transaction when a checked exception is thrown using #Transactional(rollbackFor). Here is my code:
The data access class:
#Repository
public class CustomerDao {
#Autowired
private SessionFactory sessionFactory;
public void willRollback() throws CheckedException {
sessionFactory.getCurrentSession().persist(new SpringCustomer(null, "roll back"));
throw new CheckedException();
}
}
Where CheckedException is just a simple checked exception:
public class CheckedException extends Exception {}
The service class CustomerService:
#Service
public class CustomerService {
#Autowired
private CustomerDao customerDao;
#Transactional(transactionManager = "hibernateTransactionManager", rollbackFor = CheckedException.class)
public void willRollback() throws CheckedException {
customerDao.willRollback();
}
}
Beans configurations:
#Configuration
public class BasicConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test_hibernate?useSSL=false");
ds.setUsername("root");
ds.setPassword("Passw0rd");
return ds;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource());
localSessionFactoryBean.setPackagesToScan("com.home.exception.checked");
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
hibernateProperties.put("hibernate.show_sql", "true");
hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
localSessionFactoryBean.setHibernateProperties(hibernateProperties);
return localSessionFactoryBean;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager() {
return new HibernateTransactionManager(sessionFactory().getObject());
}
}
And finally, here is my main class:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(Main.class);
try {
ctx.getBean(CustomerService.class).willRollback();
} catch (CheckedException e) {
e.printStackTrace();
}
ctx.close();
}
}
I've read many answers related to these questions, all of them suggesting to call the transactional method from outside the proxy itself, which I did. Regardless, the entity is always persisted in the database anyway and the transaction is not rolled back.
Any help would be appreciated.
UPDATE: As per #kavithakaran-kanapathippillai answer, I debugged the TransactionAspectSupport.completeTransactionAfterThrowing() method, and the following methods as well, and found that the rollback logic is executed. The entity still appears when querieng the db though.
So, I enabled db logging to see what queries are run, and I found the following:
2020-06-28T07:29:48.516038Z 391 Query SET autocommit=0
2020-06-28T07:29:48.520253Z 391 Query insert into spring_customer (name) values ('roll back')
2020-06-28T07:29:48.524969Z 391 Query rollback
2020-06-28T07:29:48.526026Z 391 Query SET autocommit=1
I don't know why this happens but it looks like the Spring rollback is working fine.
UPDATE2: The problem was due to my table was using the MyISAM engine (non-transactional engine). Once I changed it to InnoDB (a transactional engine), the record is not persisted anymore.
The following method is where spring checks whether to rollback when an exception is thrown. Class name is TransactionAspectSupport. You can put a break point and see whether txInfo.transactionAttribute.rollbackOn(ex) is evaluating to true
protected void completeTransactionAfterThrowing(#Nullable TransactionInfo txInfo,
Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
.....
if (txInfo.transactionAttribute != null &&
txInfo.transactionAttribute.rollbackOn(ex)) {
Reference:
TransactionAspectSupport.java

Spring Boot Hikari Multiple Database Autowiring Failing

I am trying to implement multiple database with Spring Boot Hikari CP. I am getting
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: hikari_primary,hikari_secondary
For your reference I am attaching my spring boot datasource configuration files,
please dont go over primary and secondary naming conventions(they dont represent priorities), my requirement it to have two connection pool for two different databases.
Any help is appreciated
1.application.properties
spring.datasource.dataSourceClassName=com.microsoft.sqlserver.jdbc.SQLServerDataSource
primary.spring.datasource.url=jdbc:sqlserver://xxx.xxx.xxx.xxx:1433;DatabaseName=training
primary.spring.datasource.username=training
primary.spring.datasource.password=training
primary.spring.datasource.poolName=hikari_primary
primary.spring.datasource.maximumPoolSize=5
primary.spring.datasource.minimumIdle=3
primary.spring.datasource.maxLifetime=2000000
primary.spring.datasource.connectionTimeout=30000
primary.spring.datasource.idleTimeout=30000
primary.spring.datasource.pool-prepared-statements=true
primary.spring.datasource.max-open-prepared-statements=250
secondary.spring.datasource.url=jdbc:sqlserver://xxx.xxx.xxx.xxx:1433;DatabaseName=dev_xxxxx_core_v3
secondary.spring.datasource.username=developer
secondary.spring.datasource.password=Developer
secondary.spring.datasource.poolName=hikari_secondary
secondary.spring.datasource.maximumPoolSize=50
secondary.spring.datasource.minimumIdle=30
secondary.spring.datasource.maxLifetime=2000000
secondary.spring.datasource.connectionTimeout=30000
secondary.spring.datasource.idleTimeout=30000
secondary.spring.datasource.pool-prepared-statements=true
secondary.spring.datasource.max-open-prepared-statements=300
2. PrimaryDataSourceConfig.java
#Configuration
public class PrimaryDataSourceConfig {
#Value("${primary.spring.datasource.username}")
private String user;
#Value("${primary.spring.datasource.password}")
private String password;
#Value("${primary.spring.datasource.url}")
private String dataSourceUrl;
#Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
#Value("${primary.spring.datasource.poolName}")
private String poolName;
#Value("${primary.spring.datasource.connectionTimeout}")
private int connectionTimeout;
#Value("${primary.spring.datasource.maxLifetime}")
private int maxLifetime;
#Value("${primary.spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
#Value("${primary.spring.datasource.minimumIdle}")
private int minimumIdle;
#Value("${primary.spring.datasource.idleTimeout}")
private int idleTimeout;
#Bean(name="hikari_primary")
public HikariDataSource getHikariDataSourcePrimary() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName", poolName);
configProps.put("maximumPoolSize", maximumPoolSize);
configProps.put("minimumIdle", minimumIdle);
configProps.put("minimumIdle", minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
3. SecondayDataSourceConfig.java
#Configuration
public class SecondaryDataSourceConfig {
#Value("${secondary.spring.datasource.username}")
private String user;
#Value("${secondary.spring.datasource.password}")
private String password;
#Value("${secondary.spring.datasource.url}")
private String dataSourceUrl;
#Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
#Value("${secondary.spring.datasource.poolName}")
private String poolName;
#Value("${secondary.spring.datasource.connectionTimeout}")
private int connectionTimeout;
#Value("${secondary.spring.datasource.maxLifetime}")
private int maxLifetime;
#Value("${secondary.spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
#Value("${secondary.spring.datasource.minimumIdle}")
private int minimumIdle;
#Value("${secondary.spring.datasource.idleTimeout}")
private int idleTimeout;
#Bean(name="hikari_secondary")
public HikariDataSource getHikariDataSourceSecondary() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName", poolName);
configProps.put("maximumPoolSize", maximumPoolSize);
configProps.put("minimumIdle", minimumIdle);
configProps.put("minimumIdle", minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
4. Application.java
#SpringBootApplication
#ComponentScan("com.xxxx.springsql2o")
#EnableAutoConfiguration
public class Application
{
public static void main( String[] args )
{
SpringApplication.run(Application.class, args);
}
#Autowired
#Qualifier("hikari_primary")
DataSource hikariDataSourcePrimary;
#Autowired
#Qualifier("hikari_secondary")
DataSource hikariDataSourceSecondary;
#Bean(name= "primary_db")
public Sql2o getPrimarySql2o()
{
return new Sql2o(hikariDataSourcePrimary);
}
#Bean(name= "secondary_db")
public Sql2o getSecondarySql2o()
{
return new Sql2o(hikariDataSourceSecondary);
}
}
Spring boot is auto-configuring your application via #EnableAutoConfiguration (note that this annotation is already included in the composed #SpringBootApplication annotation). So my guess would be that you have some dependency that spring is trying to auto-configure (e.g. JPA) which uses/needs DataSource. If you can live with this, you can add #Primary to on of your DataSource Bean provider methods in order to satisfy that dependency.
So, for instance:
#Bean(name="hikari_primary")
#Primary
public HikariDataSource getHikariDataSourcePrimary() {...
Even if this should work, it would be recommended to remove auto-configuration for e.g. JPA or whatever spring boot is trying to auto-configure but you don't use/need and configure everything manually as it suits your application needs. Have two databases is certainly a custom configuration and does not conform to the spring boot easy-out-of-the-box approach.

Vaadin 7 & Spring Boot : SqlContainer doesnot work with Filter

Spring Boot: 1.3.2.RELEASE
Vaadin : 7.6.3
Mysql: mysql-java-connector-5.1.38
HikariCP: 2.4.3
So I create a Table with only id and name columns. Then a vaadin componant Table to show the content of table. Until now everything is fine. Then I tried to filter the table with a vaadin Filter, and it works if the connection pool is com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool, and doesnot if it is com.vaadin.data.util.sqlcontainer.connection.J2EEConnectionPool which I use HikariCP as the data source.
DBConfig.java
#Configuration
#EnableTransactionManagement
public class DatabaseConfig {
#Bean
public HikariConfig hikariConfig() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/test?autoReconnect=true&useSSL=false");
config.setUsername("root");
config.setPassword("root");
config.setMaximumPoolSize(20);
config.setPoolName("connectionPool");
return config;
}
#Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource(hikariConfig());
return dataSource;
}
ApplicationUI.java
#Autowired
private J2EEConnectionPool connectionPool;
#Override
protected void init(VaadinRequest arg0) {
TableQuery tq = new TableQuery("medecine", connectionPool);// it does not work if I use HikariCP as the J2EEConnectionPool
JDBCConnectionPool pool = new SimpleJDBCConnectionPool("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/test?autoReconnect=true&useSSL=false", "root", "root");
TableQuery tq = new TableQuery("medecine", connectionPool);// it works if I use **SimpleJDBCConnectionPool**.
container = new SQLContainer(tq);
table = new Table();
table.setContainerDataSource(container);
table.setWidth(100, Unit.PERCENTAGE);
mainLayout.addComponent(table);
Button addFilter = new Button("filter", new ClickListener() {
private static final long serialVersionUID = -7071683058766115266L;
#Override
public void buttonClick(ClickEvent arg0) {
Filter filter = new SimpleStringFilter("name", "medecine2", true, false);
container.addContainerFilter(filter);
}
});
mainLayout.addComponent(addFilter);
}
The reason I choose HikariCP as the data source is for a better performance and I'm not sure that the vaadin connection pool will be better than HikariCP. Or, it is limited to vaadin connection pool for the filtering function?

Exception when embedded Cassandra running multiple test cases: Keyspace ** does not exist

The schema creation is done inside the target class SimpleRepo.java.
public class SimpleRepo {
private Cluster cluster;
private Session session;
private String keyspace = "app";
private String table = "myTable";
#Autowired
public SimpleRepo(Cluster cluster) {
this.cluster = cluster;
}
#PostConstruct
private void init() {
session = cluster.connect();
createSchema();
}
public void createSchema() {
.....
}
}
When running the SimpleTest.java with one test case inside, it will pass. When running with two cases inside, only the first one passes and the second one throws out the exception: "com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace app does not exist".
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class, SimpleRepo.class})
#TestExecutionListeners({CassandraUnitTestExecutionListener.class, DependencyInjectionTestExecutionListener.class})
#EmbeddedCassandra
public class SimpleTest {
#Autowired
private SimpleRepo simpleRepo;
#Test
public void testSave() throws Exception {
......
}
#Test
public void testDel() throws IOException {
......
}
}
#Configuration
public class TestConfig {
#Bean(destroyMethod = "shutdown")
public Cluster cluster() throws ConfigurationException, TTransportException, IOException, InterruptedException{
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
Cluster cluster = Cluster.builder()
.addContactPoints("127.0.0.1")
.withPort(9142)
.build();
return cluster;
}
}
Why the keyspace created inside createSchema() would disappear when running the second test case? How to fix this problem? Thanks for any guidance.
CassandraUnitTestExecutionListener calls cleanServer() after each test. That calls EmbeddedCassandraServerHelper.cleanEmbeddedCassandra() which drops all non-system keyspaces.
Your code creates your keyspace only once, in #PostConstruct, so only 1st test case can use it.
It looks like you should use #CassandraDataSet to initialize a keyspace for each new test
https://github.com/jsevellec/cassandra-unit/wiki/Spring-for-Cassandra-unit