I have a table and view and created a corresponding JPA for these. I want to both table and view.
-- account table
CREATE TABLE account(
user_id serial PRIMARY KEY,
username VARCHAR (50) UNIQUE NOT NULL,
password VARCHAR (50) NOT NULL,
email VARCHAR (355) UNIQUE NOT NULL,
created_on TIMESTAMP NOT NULL,
last_login TIMESTAMP
);
-- account view
create view account_view as
select user_id, email, last_login from account;
I added an integration test. It works fine for the table, however, the view does not have any data.
How can I make sure that when a row is inserted in the base table, the view also gets updated in integration test? I maybe missing some configuration. How do we add the view definition during integration test?
#DataJpaTest
public class AccountRepositoryIntegTest {
#Autowired
AccountRepository accountRepository;
#Autowired
AccountViewRepository accountViewRepository;
#Test
public void testFindByUserid(){
Account account = Account.builder()
.email("abc#gmail.com")
.username("abc")
.password("abc")
.createdOn(new Date())
.lastLogin(new Date())
.build();
Account persistedAccount = accountRepository.save(account);
Account fetchedAccount = accountRepository.getOne(persistedAccount.getUserId());
List<AccountView> accountViews = accountViewRepository.findAll();
assertThat("view has data", accountViews.size(), is(Matchers.greaterThan(0)));
assertThat(fetchedAccount.getUserId(), is(equalTo(persistedAccount.getUserId())));
}
}
As #marek.kapowicki said, the view is treated as table in JPA. This point helped me solving the problem.
When the application is running, the view data is populated by the database server. Therefore, the repository of the view is trying a select query, it gets the data.
However, during integration test, the tables are created as per the definition of the entity. The view will also be created as a table without the view definition. So, whenever you save data in the parent table, the view will not be updated.
Two ways to solve this:
create all the tables using sql scripts before running the integration tests. That way you can define the view and this script will be used to setup the database.
Create a custom entity and repository for the view that will be used only for testing. Create a custom class to process repository actions on the parent table and whenever there is an DML changes in the parent table, make sure to update the view entity according to view definition. That way, parent and view table will be in sync.
I went ahead with the second approach and it worked for me. I did not try the first approach, but it should work.
Related
I want to create a rest API that will take the stored procedure name as input with the procedure parameter as a rest API parameter. while I create an entity class I just get one table access for example I have a customer table but my stored procedure access different tables and views it gives me an error: "invalid column name id" this is because the executed procedure doesn't use columns and table that we mentioned in the entity class.
I want to create this API with spring rest using spring mvc and jpa.
I also want to authenticate if the system id exists in the table or not, example tablename:customer { sysid : ram, procedure_name:sp_byid }.
for now, How it work is check the systemid input with table(customer) used in entity classes , I am not even able to get a single data ie procedure_name as we cant pass "select colname from table name where id = x" in #query annotation. If a record exists we want to check what stored procedure mapped with the id. sp_byid is the stored procedure that gets data from different table "user" but we are not able to do that due to entity class not having the same table as used by stored procedure and gives error of "invalid column name id".
this is the use case example of one stored procedure with only one record I have many to be added.
how do we work with this, is there any alternative to jpa to work without entity and just pass custom queries?
example rest URL : localhost:8080/get/systemid/procedurename
I found an answer to do a custom query is by making a custom repository to get data via Entitymanager. but custom query returns a query type which can be converted to list of object. you cant get key-value pair like entity.
I'm running into an issue when running my SpringBootTests.
The tests are using an H2 database, so they recreate the schema every time they run. For one of my entities, Hibernate doesn't add auto_increment to the id column.
I can't find any relevant differences between the failing entity (Payment) and others that work correctly (e.g. Invoice). They all have the following annotations on the id field:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
I set spring.jpa.show-sql=true, and this is what I see:
The Payment entity shows create table payment (id bigint not null,
The Invoice entity shows create table invoice (id bigint not null
auto_increment,
In fact, I tried copying the Payment class to Payment2, and Payment2 did not have the issue:
The Payment entity shows create table payment (id bigint not null,
The Payment2 entity shows create table invoice (id bigint not null
auto_increment,
Given this, I believe I should be looking for an overriding configuration that's not in the Payment class, but that somehow removes auto_increment from just that one class.
Does anyone know what could be causing this?
These are the versions of the libraries involved:
Spring Boot version 2.1.8
Hibernate 5.3.11
H2 Database 1.4.200
I finally figured it out. This assumption was correct:
I should be looking for an overriding configuration that's not in the Payment class, but that somehow removes auto_increment from just that one class.
Turned out that a colleague had added a "light" read-only version of the Payment entity called PaymentLight. It read from the same payment table but without any of the joins. The id field in the PaymentLight class did not have the #GeneratedValue annotation.
I haven't checked this, but I assume Hibernate merged the two entities (the light class was a subset anyway) and used the #GeneratedValue configuration from one of them (likely alphabetical order).
I have very simple spring boot app which is using h2 database on local. I have schema-h2.sql which has below script -
CREATE TABLE USERS(
userid NUMBER(10,0) AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(256) UNIQUE NOT NULL,
fname VARCHAR(256),
lname VARCHAR(256)
);
And in application-default.properties -
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MSSQLServer;DB_CLOSE_DELAY=-1;DB_CLO SE_ON_EXIT=false
When I look at /h2-console, I can see table being created but it is missing unique constraint on email column.
I even tried adding alter statement in schema-h2.sql script to create constraint but that doesn't work either -
ALTER TABLE USERS ADD CONSTRAINT UQ_EMAIL UNIQUE(email);
Also, when run those script in h2-console it creates unique constraint correctly so it does seem like something to do with how spring executes them.
When debugging I can see that Alter statement gets executed without any exception in ScriptUtils class.
Not sure how to take this forward so any help will be greatly appreciated.
For anyone else coming across this, I was able to fix it by altering my JPA annotation. Adding the unique parameter in addition to creating the constraint in h2-schema.sql allowed the constraint to actually be created.
#Column(name = "email", unique = true, nullable = false)
private String email;
If schema is beign created by spring boot with schema.sql file, make sure to disable hibernate schema auto create:
spring.jpa.hibernate.ddl-auto=none
Otherwise, Hibernate will overwritte it everytime the app starts.
Using Hibernate (4.2.7.SP1), Spring and Oracle. Noticed that when the last line in the method (JPQL UPDATE) is executed, but before the #Transactional methods ends, dev's name A is commited to the database and it is visible(in selects from another connection)!
#Transactional
public void doInTransaction()
{
User user = userDao.findById("dev");
user.setName("A");
userDao.getEntityManager().createQuery("UPDATE User set name='B'").executeUpdate();
}
Note that User is a subclass of Person, InheritanceType.JOINED, i.e. there are two tables involved, the name field is inherited from Person.
Found some information here http://in.relation.to/Bloggers/MultitableBulkOperations explaining how hibernate performs the UPDATES and DELETES, and that for inheritance tables it creates temporary tables prefixed with HT_.
Performed some debugging, the issue as I see it can be represented in two lines:
update ILC_PERSON set name = 'A';
create global temporary table HT_ILC_PERSON_USER (id varchar2(255 char) not null) on commit delete rows;
-- bellow should execute the JPQL UPDATE User set name='B'
What happens is that when the DDL for creating the temporary table is executed, oracle commits automatically the previous DML.
Question:
is this a Hibernate bug?
is there some misconfiguration in the project (using LocalContainerEntityManagerFactoryBean with JpaTransactionManager)?
does this simple means we cannot use JPQL UPDATE/DELETES for entities with InheritanceType.JOINED in one transaction?
something else?
I've Table uses Trigger and sequence to set its PK column.
The Hibernate mapping strategy for its Pk is assigned..
This yields in session.save(obj) returns object with id=0
How to make it returns the correct assigned PK value.
session.getIdentifier() doesn't work!
assigned means: Nobody generates the ID, the ID is set explicitely in the entity before persisting it.
What you want to do is impossible. Hibernate would have to insert an entity without knowing its ID, then the database would generate the ID, and Hibernate would have to reload the entity from the database to know its ID. But how would it reload the entity without knowing its ID?
The native generator does the same thing, and it works because the database provides a getLastGeneratedId() method which allows getting the IOD that the database has generated. But you can't do that with Oracle and a trigger.
Remove the trigger from the database, use the sequence generator, and everything will be fine.