Problem with DAO save method when using dbunit - java

I have a class that tests adding a group to a database:
class GroupDAOTest extends TestCase {
private IDatabaseTester databaseTester;
private GroupDao groupDao;
#BeforeEach
protected void setUp() throws Exception
{
databaseTester = new JdbcDatabaseTester("org.postgresql.Driver",
"jdbc:postgresql://localhost:5432/database_school", "principal", "school");
String file = getClass().getClassLoader().getResource("preparedDataset.xml").getFile();
IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File(file));
databaseTester.setDataSet(dataSet);
databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
databaseTester.onSetup();
groupDao = new GroupDao();
}
#Test
void add() throws Exception {
groupDao.save(new Group("NEW_GROUP"));
IDataSet databaseDataSet = databaseTester.getConnection().createDataSet();
ITable actualTable = databaseDataSet.getTable("groups");
String file = getClass().getClassLoader().getResource("GroupDao/add.xml").getFile();
IDataSet expectedDataSet = new FlatXmlDataSetBuilder().build(new File(file));
ITable expectedTable = expectedDataSet.getTable("groups");
Assertion.assertEquals(expectedTable, actualTable);
}
And here is the method "groupDao.save (new Group (" NEW_GROUP "));" must add a group with id = 4, name = "NEW_GROUP". Once the test passed, but when I ran it again and again, the group was added, but for some reason the id grew by one. And for some launch it was already like this:
[![enter image description here][1]][1]
Checked groupDao.save () - everything is fine, tried changing databaseTester.setSetUpOperation (DatabaseOperation ***), but it didn't help.
Can you tell me where the problem is, maybe I'm just not clearing something?
And just in case my dao method:
#Override
public void save(Group group) {
try (Connection connection = connectionProvider.getConnection();
PreparedStatement statement = connection.prepareStatement(SAVE_NEW_RECORD)) {
statement.setString(1, group.getName());
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
And table schema:
CREATE TABLE groups
(
group_id serial PRIMARY KEY,
group_name VARCHAR(10) UNIQUE NOT NULL
);

Once the test passed, but when I ran it again and again, the group was added, but for some reason the id grew by one.
The issue is not with your code or configuration. PostgreSQL serial field type is auto-increment. It adds 1 to the field value each time saving row to the table.
Use the dbUnit ValueComparer assertion instead to compare with greater than or equal to instead of the assertion method you currently using which compares only on equality.
http://dbunit.sourceforge.net/datacomparisons/valuecomparer.html

Related

JPA Define custom error message to multiple columns unique constraint violation

I've a JPA Entity which has a unique constraint based on multiple columns, I'm trying to handle and generate a human friendly error message but somehow spring/hibernate is stealing the exception from me
#Entity
#Table(name = "columns", uniqueConstraints = {#UniqueConstraint(columnNames = {"position", "board_id"}, name = UNIQUE_COLUMN_BOARD_POSITION)})
public class Column {
//some stuff
}
Then i have the service class
#Transactional
#NonNull
public Column update(long columnId, #NonNull Column transientColumn) {
Column persistentColumn = get(columnId);
persistentColumn.setTitle(transientColumn.getTitle());
persistentColumn.setPosition(transientColumn.getPosition());
try {
return columnRepository.save(persistentColumn);
} catch (Exception e) {
System.out.println(e); //NEVER REACHES HERE
}
return transientColumn;
}
I'm writing my test clases so I autowired the service class, I intentionaly try to update a column to break the unique contraint, hibernate stops the update throw a giant exception... BUT WONT LET ME CAPTURE THE EXCEPTION
I've debugged line by line, the code reaches the save line, then spring/hibernate code starts to run, after that the exception is thrown but my catch clause never gets it and the test finishes
here is the test class
#Test
void testFailUpdateDueRepeatedPosition() {
Column column = new Column(persistentBoard, "column", 1);
Column column2 = new Column(persistentBoard, "column", 2);
column = columnRepository.save(column);
column2 = columnRepository.save(column2);
column2.setPosition(1);
columnService.update(column2.getId(), column2);
columnRepository.delete(column);
}
I don't care if I can't catch this exception all i want is to have a custom message which i can set in case it happens

Flyway Java Migrations: insert BLOB into Postgresql

I am using Flyway for all database migrations. It is time to handle binary data (images) when migrating. I am using Postgresql and Spring Data JPA.
First I had this field resulting in db column photo oid using Postgresql
#Entity
public class Person {
// omitted
#Lob
private byte[] photo;
}
My migration scripts look something like this
V1__CREATE_DB.sql
V2__INSERT_PERSON.sql
V3__INSERT_PHOTO.java
At first I did not manage to successfully migrate (update) a person with photo using JdbcTemplate. Later I found out that I could change the type oid to bytea by doing this.
#Lob
#Type(type = "org.hibernate.type.BinaryType")
private byte[] photo;
I then made the migration code looks like this
public void migrate(Context context) throws IOException {
JdbcTemplate template = ...
List<String> locations = ... // photo physical locations/paths
for(String location: locations) {
InputStream image = ... // from location
Long id = ... // get id from image name
template.update("UPDATE person SET photo = ? where id = " + id,
new Object[] { new SqlLobValue(image.readAllBytes(), new DefaultLobHandler()) },
new int[] { Types.BLOB }
);
}
}
This V3__ migration works as expected however
Is there a better way to implement this migration and should I be able to also do this for oid and in that case how?
Is there a reason for not choosing bytea over oid except for obvious storage capacity differences?
After almost breaking Google I finally managed to find a solution for how to update column photo oid with JdbcTemplate.
DefaultLobHandler lobHandler = new DefaultLobHandler();
lobHandler.setWrapAsLob(true);
jdbcTemplate.execute("UPDATE person SET photo = ? where id = ?", new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
lobCreator.setBlobAsBinaryStream(ps, 1, image, image.available());
ps.setLong(2, id);
}
}

NativeQuery to DELETE duplicate Oracle SQL Db table entries does not work (see code)

I'm trying to write code for checking a table for duplicate rows, and deleting all but one of the flagged rows(keep one, delete the duplicates). I'm using JPA NativeQuery as below with the following SQL command:
#PersistenceContext private EntityManager em;
public void findandDeleteDupDogs() {
String deleteString =
"DELETE FROM DOGS";
String wherestring = "WHERE rowid not in";
String selectminstring = "(SELECT MIN(rowid)";
String fromstring = "FROM DOGS";
String groupbystring = "GROUP BY NAME, SPECIES)";
String sqlString =
String.format(
"%s %s %s %s %s ",
deleteString,
wherestring,
selectminstring,
fromstring,
groupbystring);
try {
Query query = em.createNativeQuery(sqlString);
} catch (Exception e) {
log.error(e.getMessage());
}
}
Function to SELECT * FROM TABLE to check if deletion has gone through:
public List<Dog> selectAll(){
String selectString = "SELECT * FROM DOGS";
Query query = em.createNativeQuery(selectString);
try {
List<Dog> results = query.getResultList();
return results;
} catch (Exception e) {
log.error(e.getMessage());
return Collections.emptyList();
}
}
My Junit test is as below testing the code's workability:
#Before
public void setUp() throws InterruptedException {
batchRepo.save(batch);
propertyRepo.save(property);
dog1 = createSameDog();
dog2 = createSameDog();
dog3 = createSameDog();
dog4 = createSameDog();
dog5 = createDifferentDog();
DogRepo.save(dog1);
DogRepo.save(dog2);
DogRepo.save(dog3);
DogRepo.save(dog4);
DogRepo.save(dog5);
}
The first test which tested the SELECT statement worked fine:
#Test
public void testSelectStatement(){
assertThat(DogRepository.selectAll().size()).isEqualTo(5);
}
The Second test failed:
#Test
public void deletedupdogs() {
DogRepository.findandDeleteDupDogs();
assertThat(DogRepository.selectAll().size()).isEqualTo(2);
}
This is the error:
org.opentest4j.AssertionFailedError:
Expecting:
<5>
to be equal to:
<2>
Which to my understanding, meant the query for function findandDeleteDupDogs() did nothing at all, and all five dogs(duplicates included) still exist.
I can't seem to understand what I did wrong, and would appreciate some fresh eyes looking at this, thanks!
You should call query.executeUpdate(). Now you're only creating the queries without actually running them.

How to write a test case for method which creates the table and add values

I'm setting up java project where user enter his details and the data will be saved in the the database bellow is my code:
public String CreateUserDetails() throws SQLException, JsonProcessingException
{
iterationResourse = new IterationResourse();
dbcon = DatabaseConnection.getInstance();
iteratinDetails = IterationDetailsParser.getInstance();
try {
String sqlUser = "INSERT INTO user (User_Id,Username,Active_Indi)VALUES(?,?,?)";
PreparedStatement statement = (PreparedStatement) dbcon.con.prepareStatement(sqlUser);
statement.setString(1, iterationResourse.ConvertObjectToString(iteratinDetails.getUserId()));
statement.setString(2, iterationResourse.ConvertObjectToString(iteratinDetails.getUserObj()));
statement.setBoolean(3, true );
statement.executeUpdate();
statement.close();
System.out.println("user created");
st = "user created";
} catch (SQLException e)
{
System.out.println("user id alredy exits");
userIdExits = false;
ObjectMapper mapperUser = new ObjectMapper();
JsonNode rootNode = mapperUser.createObjectNode();
((ObjectNode) rootNode).put("Response", "User ID alreday exits");
String jsonString = mapperUser.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
System.out.println(jsonString);
iterationResourse.response = jsonString;
st = "Response\", \"User ID alreday exits";
}
return st;
}
I have to write a test case for the above code i have tried the fallowing code. i am trying to mock all the objects that i am trying to use form the other class , the expected result should be string that returns "User created" . but i am unable the get the expected result based on the current code.
public class UserDatabaseTest {
User user = null;
IterationResourse iterationResourse;
DatabaseConnection db;
IterationDetailsParser iterationDetails ;
#Before
public void setUp()
{
iterationResourse = mock(IterationResourse.class);
db = mock(DatabaseConnection.class);
iterationDetails = mock(IterationDetailsParser.class);
user = new User();
}
#Test
public void test() throws JsonProcessingException, SQLException {
Object Object = "3";
String value = "3";
when(db.getInstance().GetDBConnection()).thenReturn(db.getInstance().GetDBConnection());
when(iterationDetails.getUserId()).thenReturn(Object);
when(iterationResourse.ConvertObjectToString(Object)).thenReturn(value);
assertEquals(user.CreateUserDetails(), "user created");
}
}
There are two cases to be written here.
CreateUserDetails return "user created"
Else return "User ID already exists" (i fixed the two typos)
As stated in the comments you should really abstract your DAO layer. But at a high level, you want to mock the DatabaseConnection and return mocks for anything it may return. Doing this prevents NPE's when calling your code base.
Once your mocks are in place the test should return "user created". For the second test have one of your mock throw an SQLException and you can test that "User ID already exists" is returned. I would probably just pass iteratinDetails as a parameter, seems like a dependency for this method.
Lastly, you should not be testing that your code has created database tables and populated them correctly. As long as the data you are passing in (which is something you can test) you should have faith that SQL is going to execute scripts as intended. If you really wanted to get crazy, you could do some mocking to ensure that the statement was prepared properly. IMO that's overkill.
Goodluck!

Reset sequence in DBUnit?

I want to reset the Database AND sequences after each test in Java+DBUnit/.
I've seen this question but doesn't have the code solution I am struggling to get.
How to use Oracle Sequence Numbers in DBUnit?
I've found the answer, it was in the Official Documentation. It was as easy as in the dataset you are using to prepare the database, add a reset_sequences attribute with a list of the ones you want to reset.
<?xml version='1.0' encoding='UTF-8'?>
<dataset reset_sequences="emp_seq, dept_seq">
<emp empno="1" ename="Scott" deptno="10" job="project manager" />
....
</dataset>
This solution is not working perfectly, as it didn't really reset the sequence, only simulates the reset on the inserted rows. If you want to effectively reset it, you should execute some commands. I've extended the DatabaseOperation for that purpose with this class.
public static final DatabaseOperation SEQUENCE_RESETTER_POSTGRES = new DatabaseOperation() {
#Override
public void execute(IDatabaseConnection connection, IDataSet dataSet)
throws DatabaseUnitException, SQLException {
String[] tables = dataSet.getTableNames();
Statement statement = connection.getConnection().createStatement();
for (String table : tables) {
int startWith = dataSet.getTable(table).getRowCount() + 1;
statement.execute("alter sequence " + table + "_PK_SEQ RESTART WITH "+ startWith);
}
}
};
public static final DatabaseOperation SEQUENCE_RESETTER_ORACLE = new DatabaseOperation() {
#Override
public void execute(IDatabaseConnection connection, IDataSet dataSet)
throws DatabaseUnitException, SQLException {
String[] tables = dataSet.getTableNames();
Statement statement = connection.getConnection().createStatement();
for (String table : tables) {
int startWith = dataSet.getTable(table).getRowCount() + 1;
statement.execute("drop sequence " + table + "_PK_SEQ if exists");
statement.execute("create sequence " + table + "_PK_SEQ START WITH " + startWith);
}
}
};
I've tested the solution provided by #Chexpir, and here is an improved/cleaner way (PostgreSQL implementation) - Also note that the sequence is reset to 1 (instead of retrieving the row count)
public class ResetSequenceOperationDecorator extends DatabaseOperation {
private DatabaseOperation decoree;
public ResetSequenceOperationDecorator(DatabaseOperation decoree) {
this.decoree = decoree;
}
#Override
public void execute(IDatabaseConnection connection, IDataSet dataSet) throws DatabaseUnitException, SQLException {
String[] tables = dataSet.getTableNames();
Statement statement = connection.getConnection().createStatement();
for (String table : tables) {
try {
statement.execute("ALTER SEQUENCE " + table + "_id_seq RESTART WITH 1");
}
// Don't care because the sequence does not appear to exist (but catch it silently)
catch(SQLException ex) {
}
}
decoree.execute(connection, dataSet);
}
}
And in your DatabaseTestCase:
public abstract class AbstractDBTestCase extends DataSourceBasedDBTestCase {
#Override
protected DatabaseOperation getTearDownOperation() throws Exception {
return new ResetSequenceOperationDecorator(DatabaseOperation.DELETE_ALL);
}
}
Can you please check the below link if anyway it helps you.
How to revert the database back to the initial state using dbUnit?

Categories

Resources