Not able to execute ALTER SEQUENCE command through spring boot code - spring

I am writing a code in spring boot 1.5.22 with java 8 and oracle 11g. Here, In my repository class, I have tried to call one native query as-
#Query(value = "ALTER SEQUENCE <SEQ_NAME> RESTART START WITH 0", nativeQuery = true)
void resetSequence();
when I try to call this method in my service Impl class, I get the following error:-
java.lang.NegativeArraySizeException:-1
However, I can execute select sequence commands using java code as-
#Query(value = "select <Seq_name>.nextVal from dual", nativeQuery = true)
int getNextCount();
I don't know how exactly I can command to reset my sequence using java code/job here.

Try to add #Modifying annotation

Well, ALTER SEQUENCEis not a query, so it is not expected to work in the way you call it.
What will work is to fall back to plain JDBC and call something like this
con.createStatement().execute "ALTER SEQUENCE SEQ RESTART START WITH 0"
Two additionally remarks
the ALTER SEQUENCEis not a typical use case for spring-data so it should be used only in some supporting code for JUnit etc.
You'll have to create the sequence with MINVALUE 0 to be able to reset it (default is 1). Otherwise you get exception ORA-04006: START WITH cannot be less than MINVALUE

You can use the Entity manager to modify a sequence and execute SQL queries that the spring boot shortcuts do not allow
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#PersistenceContext
EntityManager entityManager;
public void resetSequence(String data) {
entityManager
.createNativeQuery("SELECT setval('sequence_value', "+data+", FALSE);")
.getSingleResult()
;
}

you can use entity manager to reset your sequence
ORACLE
entityManager.createNativeQuery("DROP SEQUENCE " + seqName).executeUpdate();
entityManager.createNativeQuery("CREATE SEQUENCE " + seqName + " START WITH " + size).executeUpdate();
POSTGRESQL
entityManager.createNativeQuery("SELECT setval(?, ?, true)")
.setParameter(1, seqName)
.setParameter(2, size)
.getResultList();

Related

Error during primary key generation with SPRING JPA

Hi all i have a weird problem with the generation of PK calling the jpa method save.
Here and exmpale of the snippet i have implemented:
Entity
#Table(name = "MYTABLE")
#Entity
public class EEMYTABLE{
#Id
#Column(name = "IDMYTABLE")
#GenericGenerator(name="generator" , strategy="increment")
#GeneratedValue(generator="generator")
private Long id;
...
Repository
public interface MyTableRepository JpaRepository<MYTABLE,Long> {}
Service
#Autowired
private MyTableRepository myTableRepository;
public void saveNewRecord() {
EEMYTABLE newRecord = new EEMYTABLE();
myTableRepository.save(newRecord);
Problem Steps:
i run save method from my local server. It's all ok.
i run the ssave method from my remote server. It fires the following excepion: " ORA-00001: unique constraint violated "
now if i run again the method from the server is all ok.
if i follow with another call from my local env i have the exception: " ORA-00001: unique constraint violated "
It seems that if i run the method in more than 1 env there is a desync in the internal spring generator value.
Someone with ideas to fix it?
I try some solutions read on internet but i ave not solved the problem.
The "increment" hibernate strategy is not suitable if you have several environments using the same database since it relies on incrementation of an integer inside the JVM.
This explains why your environments generate the same ids.
In your case, you have to rely on a database mechanism, e.g. sequences.
See Hibernate generator class increment vs sequence? for more details.

Check on DB with Spring JPA

I am working on a project with Spring boot. I have a problem with Spring JPA Data. I want to check if a record already exists in the db using two parameters
#Transactional
#Modifying
#Query("SELECT CASE WHEN COUNT(dfe) > 0 THEN 'true' ELSE 'false' END FROM DeployedFunctionEntity dfe WHERE dfe.function.idFunction = ?1 and dfe.gatewayId = ?2")
boolean existsByFunctionIdAndGatewayId(#Param("idFunction") Integer functionId,
#Param("gatewayId") Integer gatewayId);
boolean exist = deployedFunctionDao.existsByFunctionIdAndGatewayId(functionId, gatewayId);
I always get the following error:
org.springframework.dao.InvalidDataAccessApiUsageException: Modifying queries can only use void or int/Integer as return type!;
The nested exception is
java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type!
How can I fix it?
The #Modifying has no use for non-modifying queries, thus, must only be used for INSERT, UPDATE, DELETE or DDL queries. Since you are only making a SELECT query, remove the annotation and it'll work without problems.

Spring Boot: what is the best strategy to unit test repository methods?

We have a lot of methods with joins and fetches that look like :
#Query(
"select new com.company.user.entity.DTO.UserBillingDTO(" +
"u.id as id, " +
"u.firstName as firstName, " +
"u.lastName as lastName, " +
"e.tokenId as tokenId," +
"u.companyId as companyId," +
"e.id as entityId, " +
"u.userName as userName, " +
"u.locale as locale) " +
"from User as u " +
"join u.profiles as p " +
"join p.entity as e " +
"where u.id = :userId")
UserBillingDTO findUserForBilling(#Param("userId") String userId);
I d like to cover such methods with tests and see if our HQL queries are returning the expected results.
The question is how can i populate my local database easily to test the outcome of our methods?
One obvious way would be to create entities in the test setup using
code. But I am afraid the readability of this test will be very low.
Another idea that comes to my mind is to dump a database from our
test platform, use it to setup tests and then only run queries to
check the results.
Can you think of anything else? How could I write a maintainable test suite that is easy-to-follow for the team?
EDIT:
The post Using JUnit to perform an insert
suggests DBUnit
Populating using the repository
One possibility is to create the records within your test. This is what you described as the obvious solution. For your current code, you could do something like this:
#Test
public void findUserForBilling() {
repository.saveAll(Lists.newArrayList(
new User("1", "John", "Doe", "JDoe123", new Profile(..., new ProfileEntity(1, "token123"))),
new User("2", "Jane", "Doe", "TheJane", new Profile(..., new ProfileEntity(2, "token234")))));
UserBillingDTO dto = repository.findUserForBilling("1");
assertThat(dto.getId()).isEqualTo("1");
// ...
}
While in some cases your test data might take some place, in this case, it's only a few lines, which isn't any more than a usual unit test preparation/given scenario.
Be aware: In this type of test, you are not testing your entity mapping. If there's an issue within your entity mapping, you won't be able to tell using these tests.
Populating using a SQL file
Another possibility is to use a separate SQL file, for example user-dataset.sql within src/test/resources:
insert into user (id, firstname, lastname) values ("1", "John", "Doe");
insert into user (id, firstname, lastname) values ("2", "Jane", "Doe");
--- ...
And then you can include that dataset in your test by using the #Sql annotation, for example:
#RunWith(SpringRunner.class)
#DataJpaTest
#Transactional(propagation = Propagation.NOT_SUPPORTED)
#Sql("classpath:user-dataset.sql") // Add this
public class UserRepositoryTest {
// ...
}
You can add the #Sql annotation to your test class, or even to a single test method, for example:
#Test
#Sql("classpath:user-dataset.sql") // Add this
public void findUserForBilling() {
UserBillingDTO dto = repository.findUserForBilling("1");
assertThat(dto.getId()).isEqualTo("1");
// ...
}
Be aware: If I'm not mistaken, Spring will create the datasource once for the test class. If you execute your dataset for each test method, you'll have to add a delete statement to delete all existing records first.
If you just want one dataset for all your tests, you could even further simplify and name your dataset data.sql. Since Spring boot will automatically execute a data.sql file on your classpath if you're using an in-memory database (which could be useful for your tests), you don't even need the #Sql annotation.
Populating using DbUnit
Another approach is to choose a framework like DbUnit, which allows you to define your dataset in an XML format. You can use it in combination with Spring Test DBUnit to integrate more easily with Spring.
You have to keep in mind though that it's not as easily set up as using #Sql and you need to know an additional language, namely the XML structure for setting up datasets.
If you are not using database specific features, e.g. custom SQL functions specific to Oracle, you can set up an embedded database in the #SpringBootTest test. Annotate the test class with #AutoConfigureTestDatabase annotation e.g. to replace the default application DataSource bean with an embedded in-memory database:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
You can use either Flyway or Liquibase to populate the database as per 81.5 Use a Higher-level Database Migration Tool chapter. As per docs:
You can also use Flyway to provide data for specific scenarios. For example, you can place test-specific migrations in src/test/resources and they are run only when your application starts for testing. Also, you can use profile-specific configuration to customize spring.flyway.locations so that certain migrations run only when a particular profile is active. For example, in application-dev.properties, you might specify the following setting:

Spring JPA with native query and data projection mapping the wrong columns into the projected interface

I've got a bit of a bizarre problem that I can't figure out why it's happening. I'm sure I did something wrong, because this is my first time using a data projection and I've never had such problems using DTOs.
Pretty much I have a SELECT statemen that is returning certain columns of various data types. And I have an interface that I'm passing to the JPA Repository so it can do the interface mapping. But instead of mapping the results based on the column name (eg. 'accountnum' -> getAccountnumber()), it's mapping the columns in alphabetical order. So if 'date_of_order' is the first in the SELECT statement, its value will be returned by getAccountnumber().
I have a projected interface that looks something like this:
public interface FlatSearchResult {
String getAccountnumber();
UUID getTrackingId;
Date getDateOfOrder;
}
My model has three tables something like this:
ACCOUNT
- account_id : uuid (pkey)
- accountnumber : string
ORDERS
- order_id : uuid (pkey)
- date_of_order : timestamp
- account_id : uuid (fkey)
TRACKING
- tracking_id : uuid (pkey)
- order_id : uuid (fkey)
There's other columns in each of those tables, but they're not relevant.
I have a repository defined with a simple query:
public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
#Query( nativeQuery = true,
value = "SELECT o.date_of_order, a.accountnumber, t.tracking_id " +
"FROM account as a " +
"INNER JOIN orders as o USING (account_id) " +
"INNER JOIN tracking as t USING (tracking_id) " +
"WHERE a.accountnumber = :acctnum")
<T> Collection<T> findOrderInfoForAccount(#Param("acctnum") acctNumber, Class<T> type);
}
When I call this method, the correct rows are returned by the query. But instead of mapping using the column name (eg. date_of_order to getDateOfOrder()), it is mapping based on the order of the columns in the SELECT statement to the alphabetically-ordered methods in the interface.
So:
SELECT date_of_order, accountnumber, tracking_id
Results in:
getAccountNumber() -> date_of_order
getDateOfOrder() -> accountnumber
getTrackingId() -> tracking_id
It will consistently return in this fashion, so it's not a transient issue.
As a temporary workaround, I've reordered the columns in my SELECT statement. But I would rather not have to do this since it's like iterating through a result set and relying on column position, which just makes me twitchy....
How can I get Spring JPA to map from the result set to my interface? Do I need to annotate my projection interface's methods with something to tell Spring what column name it's referring to?
My database is Postgres. I'm using Spring 5.0.2.RELEASE and Spring-Boot 2.0.0.M7. I can adjust either of those to newer versions if needed, but nothing older. I'm using C3P0 0.9.5.2 for my connection pooling, and postgres-9.2-1002.jdbc4. All my other dependencies (hibernate, etc) are what is pulled in by this version of Spring-Boot.
Not sure if this is the correct solution because it only fits 80% of the description. But it is too long for a comment. So here we go.
I think you misunderstood #osamayaccoub or the documentation. Your property name is fine. But the columns in your select should match the java convention.
So the first attempt to fix that would be
value = "SELECT o.date_of_order as dateOfOrder, a.accountnumber as accountNumber, t.tracking_id as trackingId "
Note: This might actually work, but might break later, so read on, even if it does work
But Postgres converts everything that isn't double quoted into lower case (Oracle and MySql do similar stuff though details vary, don't know about other DBs yet). So you really should use:
value = "SELECT o.date_of_order as \"dateOfOrder\", a.accountnumber as \"accountNumber\", t.tracking_id as \"trackingId\" "
This probably doesn't work, because the Hibernate version you are using has a bug in that it converted everything to lower case.
So you should upgrade to the latest Hibernate version 5.3.13 which has the issue fixed.
This bug fix interestingly might break the version without the double quotes.
But it should work again with this PR for this Spring Data JPA issue.
The part I don't understand is, why stuff gets assigned using the column order.
I had the same problem and i solved by odering the query columns alphabetically.
In you case:
public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
#Query( nativeQuery = true,
value = "SELECT a.accountnumber, o.date_of_order, t.tracking_id " +
"FROM account as a " +
"INNER JOIN orders as o USING (account_id) " +
"INNER JOIN tracking as t USING (tracking_id) " +
"WHERE a.accountnumber = :acctnum")
<T> Collection<T> findOrderInfoForAccount(#Param("acctnum") acctNumber, Class<T> type);
}
So you will get:
getAccountNumber() -> accountnumber
getDateOfOrder() -> date_of_order
getTrackingId() -> tracking_id
Hibernate sorts the query in alphabetic order so you have to change the select to: "SELECT a.accountnumber, o.date_of_order, t.tracking_id ..." and the interface's getters should follow the same alphabetic order.

Spring Data JPA - Select row for update

I have a requirement to read the first enabled account from DB2 database table and immediately update the column to disable it. While server 1 is reading and updating the column, no other server should be able to read the same row since I want one account to be used by only one server at a time.
This is what I have so far..
Account.java
Account{
private Long id;
private Character enabled;
.............
}
AccountRepository.java
public interface AccountRepository extends JpaRepository<Account, Long>{
Account findFirstByEnabled(new Character('Y'));
}
AccountServiceImpl.java
#Service
public class AccountServiceImpl {
#Autowrired
private AccountRepository accntRepository;
#Transactional
public Account findFirstAvaialbleAccount(){
Account account = accntRepository.findFirstByEnabled(new Character('Y'));
if(account != null)
{
account.setEnabled(new Character('N')); //put debug point here
account.save(proxyAccount);
}
return account;
}
}
But this isn't working.. I've put a debug pointer in the findFirstAvaialbleAccount() method. What I was expecting is, if the debug pointer reaches that line and waiting for me to resume execution, if I run select query directly on the database, the sql shouldn't execute. It should only execute after I resume the execution on the server so that transaction is completed. But instead, running the select query directly on the database gave me the complete result set immediately. What am I missing here? I'm using DB2 if it matters.
Answering my own question... I had incorrect Select SQL running against the database. If I run the select sql with "select.. for update", then the execution waits until I hit resume on the server and transaction is complete.
SQL 1 - this executes immediately even though the transaction from server isn't complete.
select * from MYTABLE where ENABLED = 'Y';
SQL 2- this waits until the transaction from server is complete (it will probably timeout if I don't hit resume quick enough)
select * from MYTABLE where ENABLED = 'Y'
fetch first 1 rows only with rs use and keep update locks;

Resources