How to manage unique Ids with Hibernate and Flyway? - spring-boot

Here is a snippet of my entity class
#Entity
public class User {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
When using (Spring Boot + Hibernate) Spring Boot setups schema automatically including sequences like one below
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
But I am using Flyway 5.0.7 to setup my schema. And in this case I get the error below, which means sequence is not getting created.
Sequence "HIBERNATE_SEQUENCE" not found; SQL statement
I was able to fix this by creating sequence using flyway script like below
create sequence HIBERNATE_SEQUENCE start with 1001;
But now this sequence is used to generate Ids for all entities which I do not want. I want each entity to have its separate sequence.
Is it possible to create sequences using Hibernate when using Flyway? Otherwise it is not practical to manually create sequences for all entities which can be in hundreds.
Any alternative approach to handle this?

Flyway is a DB migration tool, and it does not know of any DDL/DML changes unless you tell it so (via new scripts in the locations property).
If Hibernate handles some of these changes (the sequences in your case) Flyway won't know about it and will use whatever sequence it already has knowledge about.
The normal thing to do is letting Flyway know of your changes, which includes a new sequence for a new entity for instance, just like you would do for the schema itself of your entity. My personal advice is to manage all your schema changes in one place, so if you are using Flyway, then let it be in charge of all of it.

Related

Is using #Entity annotation in Spring boot JPA creates a table?

BACKGROUND
I am new to developing API in spring boot. I have this project wherein it is connected to an Oracle DB and PostgreSQL. The Oracle DB already have an existing tables and I need to fetch some data from multiple tables and send it back as a response. The Postgres DB is where I store the users data and other some data that doesn't need to be stored in the Oracle DB. I am currently using native queries.
The Account is an entity wherein I just marked one of the columns as the #Id (It is actually not an Id but it is unique for all accounts):
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Account {
#Id
private String sampleProperty1;
private String sampleProperty2;
private String sampleProperty3;
private String sampleProperty4;
private String sampleProperty5;
}
Now I have a repository interface:
public interface IAccountRepository extends JpaRepository<Account, String> {
#Query(value = "SELECT * FROM TABLE(SAMPLE_PACKAGE.SAMPLE_FUNC_GETACCOUNTS(?1))", nativeQuery = true)
List<Account> getAllAccountsByClientNumber(String clientNumber);
}
I was able to fetch the data and JPA mapped the columns automatically to my entity. Basically I am creating an Entity (Spring boot) for the data in my Oracle DB where the only purpose of it is to map the data and send it back to the user.
QUESTIONS
Will this approach create a table in my Oracle DB? I checked the Oracle DB and there is no table. But I'm worried it might somehow create a table of ACCOUNT in the oracle DB when it is on production. If this might happen, how can I prevent it?
This scenario also applies to other functionality like fetching transaction history, creating transaction, updating the Account data that are all in the Oracle DB. Am I doing it just right or there is a better option?
Is creating an Entity without a corresponding table have a drawback in Spring boot?
Note
I know you might say that I should just use the Oracle DB and create entities based on the existing tables. But in the future of the API, it will not have a connection with the Oracle DB. I already tried using projections it was also good, but I still needed to create a Response model and mapped it then send it back to user and creating a unit tests using the projection is pretty long and it sucks haha
You can set the following property:
spring.jpa.hibernate.ddl-auto=update
update will update your database if database tables are already created and will create if database tables are not created.

Spring data. How can I update an entity, having the id modified?

Spring data jpa + postgres.
Have an entity
class Entity {
#Id
#GeneratedValue// generator from here https://stackoverflow.com/questions/60989691/how-to-manually-set-a-value-for-generatedvalue/61007375#61007375
private int id;
private String value;
}
And what I wish to do is to UPDATE an existing entity, setting a different id (be it a bad practice or not) value.
By default it of course is treated as a new entity and is attempted to be INSERTed.
Going by the flow of #Modifying seems to do the job right, but currently struggling to find if I can pass the whole entity instead of pinpointing every field:
update Entity e set e.id=?1, e.value=?2 where...
to
update Entity e set e=?1
So the questions here would be:
1. Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
2. If not, is there a way to provide the full entity to be consumed by the #Query?
Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
If you are using GenerationType.AUTO strategy for #GeneratedValue (AUTO is the default strategy), then you can set id of Entity to null before calling save. It will insert a new record with rest of the fields being the same as original. id of new record will be generated automatically by database engine.
If you are using GenerationType.AUTO strategy for #GeneratedValue
If not, is there a way to provide the full entity to be consumed by the #Query?
You can chose not use #Query. A far simpler approach can be using default save method provided by JPA repositories to directly pass an Entity object.

Spring Data Neo4J #Index(unique = true) does not working

I want to make userName property in User node as a unique.
I used below code but it doesn't create a unique constraint in the Neo4j database.
#Property(name = "name")
#Index(unique = true)
private String usreName;
FYI, I'm using the Neo4j Server version: 3.3.6 (community) With Spring Boot 2.
but if I create a constraint in the Neo4j Browser by myself, it works.
CREATE CONSTRAINT ON (user:User) ASSERT user.userName IS UNIQUE
Is there a way to force Spring Data Neo4J to create unique properties, without creating them by myself in Database?
You need to configure the auto index manager if you want the application code create the constraints.
You can find the best fitting option in the documentation:
https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#reference:indexing:creation
Just a note on this topic: Think about the auto index creation like Hibernate's DDL support. It is a helper at development time. You should not use assert and update in production environments but only validate.
Reason
In Spring Data Neo4j 4, index management concerns were removed from
the mapping framework entirely.
(from Index Management in Spring Data Neo4j)
Solution
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void createIndexesAndConstraints() {
Session session = sessionFactory.openSession();
Result result = session.query("CREATE INDEX ON :User(userName)", Collections.EMPTY_MAP);
}
You can configure the mode our auto index manager works in through application.properties
spring.data.neo4j.auto-index=validate # or
# spring.data.neo4j.auto-index=update
# spring.data.neo4j.auto-index=assert
Default mode is none. Apart from that, what #meistermeier says applies.
Also, Neo4jOperations was deprecated in SDN 4 something and has been removed in SDN 5. Use Session instead for operations "near" the database.
Thank you #ThirstForKnowledg for your answer. But I have 3 other Questions:
1- I'm using Spring Boot 2, and I can not see Neo4jOperations in my classpath to import it.
2- Should I put this in my Entity node or in another bean?
3- What about after running my application two or more times? I think it would cause an exception for the second time or more.

Use of Hibernate when constraints are not defined

We have a requirement to redesign one application but without doing any modification on the database.
The current code uses plain jdbc to query data. We are planning to implement hibernate instead as part of the redesign.
The problem is the current database doesn't have any constraints present at the database level.
For example if there are two entities Course and Review. The current database doesn't have any mapping between them.
Now in this case if I implement hibernate and try to have #OneToMany between Course and Review, without having those constraints in the database, will that work out fine?
1 To stop hibernate from changing you scheme set parameter
spring.jpa.hibernate.ddl-auto = none
81.2 Initialize a Database Using Hibernate
2 Set GeneratedValue strategies to use existing sequences
#SequenceGenerator(name="my_seq", sequenceName="my_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
private Long id;
This way Hibernate will change nothing in your schema. It will not create any sequences, tables, foreign keys, constraints e.t.c. Just use existing structure and fall if something wrong with it )
If there is no cascading from Course to Reviews and you delete Course the delete will go through without any complaints. With constraint present, DB would have complained about existing child records(Review in this case).
So special care needs to be taken specifying the cascade.

Postgresql and Spring Roo Binding serial type for ID

I am having trouble using the Postgresql serial type in Spring Roo. What I want is to have the an auto-incrementing id column which will work with the auto generated entity classes in Roo.
The Postgresql sequences, which are generated with the default way of doing things in Spring Roo, work fine within the spring application. But sometimes I have to manually insert rows in the database using sql. (the sequences dont seem to work properly when I do an INSERT INTO... statement). If I could use serial type, then manual INSERTS are easy.
For example I have an office entity and and employee entity. There is a many-to-one relationship between employees and offices.
Here is my class for the Office entity.
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class Office {
#Id
#Column(name="officeid", columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long officeid;
/**
* Office Name
*/
#NotNull
#Size(max = 100)
private String name;
}
While this does work when my test inserts an office record, it fails when an employee record is inserted since the officeid foreign key value is null. (I guess it needs to flush between the office insert and the employee insert, but the auto-generate tests dont seem to do that.)
So what is the proper annotations to use to tell Roo (and hibernate/jpa) to use the serial data type, and also to work properly with inserts and relationships within the spring application?
Roo generates default JPA annotations, you must customize and setup them as needed. Note Roo guarantees your changes won't be modified.

Resources