Sequence generator in h2 does not provide unique values - spring-boot

For H2 database in my spring boot api test there is an insertion script that looks something like:
INSERT INTO STORES_TEMPLATE (ID, COUNTRY, TEMPLATE_NAME_1, STORES_1) VALUES(STORES_TEMPLATE_ID_SEQ.NEXTVAL,'country', 'template_name', 'stores');
INSERT INTO STORES_TEMPLATE (ID, COUNTRY, TEMPLATE_NAME_2, STORES_2) VALUES(STORES_TEMPLATE_ID_SEQ.NEXTVAL,'country', 'template_name', 'stores');
INSERT INTO STORES_TEMPLATE (ID, COUNTRY, TEMPLATE_NAME_3, STORES_3) VALUES(STORES_TEMPLATE_ID_SEQ.NEXTVAL,'country', 'template_name', 'stores');
The entity:
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "STORES_TEMPLATE")
public class StoresTemplate {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "STORES_TEMPLATE_ID_SEQ")
#SequenceGenerator(name = "STORES_TEMPLATE_ID_SEQ", sequenceName = "STORES_TEMPLATE_ID_SEQ", allocationSize = 1)
private Long id;
#Enumerated(EnumType.STRING)
private CountryEnum country;
private String templateName;
#Lob
private String stores;
public void setStores(List<String> stores) {
this.stores = String.join(",", stores);
}
#JsonIgnore
public List<String> getStoresAsList() {
return Arrays.stream(stores.split(","))
.distinct()
.collect(Collectors.toList());
}
}
On production oracle database everything works just fine, but on my test environment with h2 database when I try to save new entity the sequence generator gives me ids that are duplicated with the ids of the predefined by the sql migration data. What can I do to fix this issue?
What did the trick was to exclude the insertion script for the tests, but is there another possible solution?

Related

Is that possible in spring boot that join column (foreign key) with id

I want to join column without object reference. is that possible?
I want to do foreign key without object reference like that
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "REFERENCE_NO")
private String referenceNo;
#ManyToOne
#JoinColumn(name = "AUTHORITY", referencedColumnName = "id")
private Integer authority;
#ManyToOne
#JoinColumn(name = "PART_TWO_REGISTER_ID")
private Integer partTwoRegisterId;
#Column(name = "PART_TWO_ORDER_NO")
private String partTwoOrderNo;
#Column(name = "REMARKS")
private String remarks;
#Column(name = "HRM_TYPE")
private Integer hrmType;
}
If I found solve this problem, it will helpful for me.
Joining is not needed in this case. If you only need the foreign key value, then simply add the column as a #Column like any other:
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "AUTHORITY")
private Integer authority;
// other fields
// ...
}
No, I don't think that you can join columns between two entities without adding the reference of one to the related entity. You will have to create one entity class corresponding to each of your relational database table and add the reference of one to the other to establish relation.
However, I understand that you may not need all the attributes from your related table based upon your use case, and only wish to select one column from it. You can do that either by only adding required attributes in your joined table entity class (if you are sure you won't need other attributes for that table anywhere else).
Or you can use custom queries using JPQL in your repository class which selects only the required attributes from the tables that you have joined.
I will show you an example of the second way:
//Say, this is your entity class where you wish to join other table to fetch only one attribute from the joined table-
#Entity
#Table(name = "TABLE1", schema = "SCHEMA1")
public class Table1 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN")
private String table2Column;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TABLE2_COLUMN1")
private Table2 table2; //refrence of the joined table entity object
}
// And this is the joined table entity class
#Entity
#Table(name = "TABLE2", schema = "SCHEMA1")
public class Table2 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN1")
private String table2Column1;
#Column(name = "TABLE2_COLUMN2")
private String table2Column2; // The column which we want to select from the joined table
}
In your repository class -
#Repository
public interface Table1Repository extends JpaRepository<Table1, String> {
#Query("SELECT t1 FROM Table1 t1 WHERE t1.id = :id")
public List<Table1> getTable1Rows(#Param("id") String id);
#Query("SELECT t1.table2.table2Column2 FROM Table1 t1 WHERE t1.id = :id")
public String getTable2Column2(#Param("id") String id);
}
Based upon the response from Markus Pscheidt below, I agree when he said there's no need to join the entities if you only need the attribute which is a foreign key. As foreign key is already present as an attribute in your entity (or table) you are working with.
If you need to fetch any other column apart from foreign key, then you may use JPQL to fetch the exact column that you wish to select.

How to use Spring Boot CRUD API to insert data in multiple tables using one POST endpoint

How can a data be inserted using single POST endpoint in multiple tables. For example there are two tables
1. Employee
2. Department
These two tables have a primary key and foreign key relationship.
How to achieve data insertion in two tables using a single POST endpoint ?
Ok I see what you want.... your entities have to look like this...
You have to create a one to one relationship something like this:
Department entity:
#Entity
#Table
#Data
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
}
Employee entity:
#Entity
#Table
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String email;
private String address;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}
And than you can add Data on Startup like this:
#Component
public class DBSeeder implements CommandLineRunner {
#Autowired
private EmployeeRepository repository;
#Override
public void run(String... args) throws Exception {
Department dep1 = new Department();
dep1.setName("Demolition");
dep1.setDescription("Do demo");
Employee emp1 = new Employee();
emp1.setName("John Rambo");
emp1.setEmail("john.rambo#demolition.com");
emp1.setAddress("Demolition Av. 5");
emp1.setDepartment(dep1);
this.repository.save(emp1);
}
}
#Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
Employee save(Employee employee);
}
Do you also ask how the entity objects have to look like?

JPQL query / JPA / Spring boot best practice of updating many to many table

I have a user table and a city table and I have a connecting table users_cities (columns: id, user_id, city_id). A user can follow multiple cities.
From the client side i send an array of cityIds. Some might be new some might still be selected and some might have been deselected.
What is a best practice to update the users_cities table with the data? Should I just delete everything for that particular userId and insert the new array or ... ?~
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?!
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String email;
private String password;
private Boolean isGuest;
#ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
#ManyToOne()
#JoinColumn(name = "country_following_id")
private Country countryFollowing;
#ManyToMany()
private Set<City> citiesFollowing;
#ManyToMany()
private Set<Offer> offersFollowing;
}
and
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NonNull
private Long id;
private String countryCode;
private String name;
#Latitude
private Double latitude;
#Longitude
private Double longitude;
}
Should I just delete everything for that particular userId and insert the new array
Since the relation connecting users and cities does not have an identity of its own, that sounds reasonable
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?
Just clear the User.citiesFollowing collection and populate it anew (hint: since you have cityIds at the ready, you can use EntityManager.getReference() to load the cities)

findBy not working with inherited properties

I have the following model and repository:
#Entity
#Table(name = "db_user", uniqueConstraints = { #UniqueConstraint(columnNames = "email") })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_user")
#SequenceGenerator(name = "seq_user", sequenceName = "seq_user")
#Column(name = "id")
private Long id;
// ...
}
#Entity
#Table(name = "movie")
public class Movie extends AbstractItem {
// Id column inherited from AbstractItem
// ...
}
#Entity
#Table(name = "movie_user")
public class MovieOwnership extends AbstractOwnership {
#ManyToOne
private Movie movie;
// ...
}
#MappedSuperclass
public abstract class AbstractOwnership{
#Id
#SequenceGenerator(name = "seq_default", sequenceName = "seq_default")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_default")
#Column(name = "id")
private Long id;
#ManyToOne
private User owner;
// ...
}
public interface MovieOwnershipRepository extends QueryDslJpaRepository<MovieOwnership, Long> {
List<MovieOwnership> findByOwnerId(Long ownerId);
MovieOwnership findByOwnerIdAndMovie(Long ownerId, Movie movieId);
List<MovieOwnership> findByOwnerIdAndMovieIdIn(Long ownerId, Set<Long> movieIds);
}
I'm trying to use Spring's findBy requests to fetch MovieOwnerships by owner or movie, using the id field of both entities. I'm able to work directly with the owner's id, but using MovieId in my requests seems broken (I can use the whole Movie object though). In the code above, the first two findBy are fine but the last one throws this exception:
Caused by: java.lang.IllegalArgumentException: Unable to locate
Attribute with the the given name [movieId] on this ManagedType
[carrm.app.data.AbstractOwnership]
It compiles if I try with another property from Movie (like findByMovieTitle), but I can't make it work on the id.
Any idea how to solve this?
I tried the same with JpaRepository instead of QueryDslJpaRepository.
The SQL is generated correctly:
select movieowner0_.id as id1_1_, movieowner0_.owner_id as owner_id2_1_, movieowner0_.movie_id as movie_id3_1_
from movie_ownership movieowner0_
left outer join user user1_ on movieowner0_.owner_id=user1_.id
left outer join movie movie2_ on movieowner0_.movie_id=movie2_.id
where user1_.id=? and (movie2_.id in (?))
So it must be a QueryDslJpaRepository implementation bug.
I would suggest you use JpaRepository instead.

SequenceGenerator not using next value of databasesequence

I have a problem in my application. I have an Oracle database with a sequence on a table. When i view the sequence in the database it says that last_number is 33800. However, when I try inserting a new object from my application the generated id is not 33800, but rather a smaller number. My guess is that Hibernate or whatever just finds the next available number. It does happen that my application deletes rows in the table, thus causing holes in the id-sequence. So, eventually I will get an exception because an ID i am trying to insert has already been used.
How can I configure the application so that it is always the sequence's last number + 1 that is used? I thought that this was default behavior, as I cannot recall encountering this problem.
This is my entity:
#Entity
#SequenceGenerator(name = "SequenceIdGenerator", sequenceName = "MY_SEQ", allocationSize = 20)
#Table(name = "myEntity")
public class myEntity {
private Long id;
#Id
#Column(name = "id")
#GeneratedValue(generator = "SequenceIdGenerator")
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
}
you can let hibernate handle this by
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id", nullable = false)
private Long id;
you can read more about it here https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing
You are using ORACLE so for you GenerationType.SEQUENCE will work as GenerationType.IDENTITY is not supported by Oracle

Resources