Map one value to multiple columns in JPA - java

I get an input value from the user and store it into the DB.
I use JPA to write into DB.
If I need to store the input value into 2 columns in the DB, what is the annotation I must use?
Code below.
I need to store user input - (stored in Bean field UserOrderDetails.noOfItemsSelected) into 2 columns in the DB table NoOfItemsSelected and NoOfItemsDispatched.
What annotation should I use?
The catch is that I do not want to add a field for NoOfItemsDispatched in the Entity Bean.
DB Table:
create table UserOrderDetails(
TransactionId varchar(255),
CreationDateTime datetime2(),
NoOfItemsSelected int,
NoOfItemsDispatched int
)
GO
My Entity Bean:
#Entity
#Table(name="UserOrderDetails")
public class UserOrderDetails {
#Id
private String transactionId;
private Timestamp CreationDateTime;
private int noOfItemsSelected;
//Getters and Setters
}
Controller class for Reference:
class UserOrderCreationController {
#RequestMapping(value = "/createUserOrder", method = RequestMethod.POST)
public ResponseEntity<?> createUserOrder(#RequestBody UserOrderDetails userOrderDetails , #RequestHeader HttpHeaders headers) throws Exception{
//Some business logic to handle the user Order
dbrepository.save(UserOrderDetails);
return new ResponseEntity<UserOrderDetails>(userOrderDetails, HttpStatus.CREATED);
}
}
Thanks

As far as i know you just need to add annotations as below on the fields in the DAO layer entity class.if you have the same field names and column names these annotations are optional.
As the question states one value to multiple columns,before the the DAO.save is called you should be populating the value in both the fields.
#Entity
#Table(name="UserOrderDetails")
public class UserOrderDetails {
//Need to have identity generator of your wish
#Id
#Column(name = "TRANSACTION_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String transactionId;
private Timestamp CreationDateTime;
**#Column(name = "NO_OF_ITEMS_SELECTED")**
private int noOfItemsSelected;
**#Column(name = "NO_OF_ITEMS_DISP")**
private int noOfItemsDispatched;
//Getters and Setters
}
OR
Alternatively we can use Hibernate's #Formula annotation.Populate the one field which is coming from user request to the corresponding field and For the other column for which you can keep the same value by using #Formula.Look for some examples of #Formula
#Formula("noOfItemsSelected")
private float noOfItemsDispatched;

Related

Can we use Composite Primary Key Mapping in spring data elastic search

I have an entity 'Product' and I want the primary key in ES to be used as a combination of 'id' and 'name' attributes. How can we do that using spring data elastic search.
public class Product {
#Id
private String id;
#Id
private String name;
#Field(type = FieldType.Keyword)
private Category category;
#Field(type = FieldType.Long)
private double price;
#Field(type = FieldType.Object)
private List<ValidAge> age;
public enum Category {
CLOTHES,
ELECTRONICS,
GAMES;
}
}
One way to achieve this would be the following:
first rename your id property, I changed it to documentId here. This is necessary, because in Spring Data
Elasticsearch an id-property can be either annotated with #Id or it can be namend id. As there can only be one
id-property we need to get this out of the way. It can have the name id in Elasticsearch, set by the #Field
annotation, but the Java property must be changed.
second, add a method annotated with #Id and #AccessType(AccessType.Type.PROPERTY) which returns the value you
want to use in Elasticsearch.
third, you need to provide noop-setter for this property. This is necessary because Spring Data Elasticsearchsoe
not check the id property to be read only when populating an entity after save or when reading from the index.
This is a bug in Spring Data Elasticsearch, I'll create an issue for that
So that comes up with an entity like this:
#Document(indexName = "composite-entity")
public class CompositeEntity {
#Field(name="id", type = FieldType.Keyword)
private String documentId;
#Field(type = FieldType.Keyword)
private String name;
#Field(type = FieldType.Text)
private String text;
#Id
#AccessType(AccessType.Type.PROPERTY)
public String getElasticsearchId() {
return documentId + '-' + name;
}
public void setElasticsearchId(String ignored) {
}
// other getter and setter
}
The repository definition would be straight forward:
public interface CompositeRepository extends ElasticsearchRepository<CompositeEntity,
String> {
}
Remember that for every method that needs an Elasticsearch Id, you'll need to create like it's done in the entity
class.
I am not sure about spring data elasticsearch but spring jpa provides the facility of defining composite primary key by using #IdClass where we can define a separate class(let us say class A) in which we can define all the fields which we want to be a part of composite key Then we can use #IdClass(A.class) in entity class and use #Id annotation on all the fields which should be the part of the composite key
you can refer to this article, although I am not sure whether the same concept will be applicable for spring data es - https://www.baeldung.com/jpa-composite-primary-keys

How to filter by one column when that column belongs to a composite primary key in Spring

Having a DAO like this:
#Entity
#Table(name="booking")
public class BookingObject implements Serializable{
private static final long serialVersionUID = 1L;
#EmbeddedId
private CompositeId compositePK;
private LocalDate init_date;
private LocalDate end_date;
private int confirmation;
// getters and setters
And composite primary key:
#Embeddable
public class CompositeId implements Serializable{
private static final long serialVersionUID = 1L;
#NotNull
private String guest;
#NotNull
private String idhousing;
//getters and setters
So I can now call findById(new CompositeId(guest, idhousing)); from my #RestController.
The question is: what about if I want to filter by one of the composite primary key's columns, like guest.
I can't do findByguest since guest no longer exists in my DAO BookingObject. How can I get then the results of "give me all rows where guest is equal to..."
You can try with findByExample
CompositeId compId= new CompositeId();
compId.setGuest(guestVal);
BookingObject bookingObj= new BookingObject ();
bookingObj.setCompositeId(compId);
Example<BookingObject> bookExample = Example.of(bookingObj);
List<BookingObject> objList=repository.findAll(bookExample);
You can do this by declaring a query method in the repository class like
List<BookingObject> findByCompositePKGuest(String guest);
And use this method to get the data from your controller.
The method is written in standard JPA format. The method name is resolved into object and field notion and converted to an actual SQL query in the background.
For more parameters, it'll be like this
List<BookingObject> findByCompositePKGuestAndCompositePKIdhousing(String guest, String idhousing);
findByCompositePKGuestAndCompositePKIdhousing is converted to -> findBy + compositePK.guest + And + compositePK.idhousing
Another option is to use Spring JPA filter by Example
Declare findAll(Example) method in the repository class
List<BookingObject> findAll(Example<BookingObject> bookingObject);
And call this method from the controller like this
BookingObject bookingObject = new BookingObject();
CompositeId compositeId = new CompositeId();
compositeId.setGuest(guest);
bookingObject.setCompositeId(compositeId);
Example<BookingObject> bookingObjectExample = Example.of(bookingObject);
List<BookingObject> bookingObjectList = bookingRepository.findAll(bookingObjectExample);
Refer this link for more detailed answer
An alternative approach could be to create an autoincremental key. It's an integer that automatically increments itself when a new row is inserted in the table. That way the composite primary key is not needed. Only problems is that users can book the same house infinite times, on the same date etc. But it's possible to implement some logic in your servlet in order to check if that user has already booked that house.
Refer to: w3schools autoincrement

Can an entity be updated instead of being inserted using spring-data-jpa when entity ID is not known without issuing a query?

We have a Spring Boot/Data-JPA (1.3.3.RELEASE) application using Hibernate implementation where a CSV file is read and inserted into a database table called FIRE_CSV_UPLOAD. For records that are already present we just update them.
We retrieve record ID by querying for unique key (a combination of three columns) but this approach is inefficient for thousands of record in CSV file.
My question is how to update record without querying the table for unique key? If I do not query for ID then the record will be inserted instead of update.
I know one way which is to retrieve all records and store their unique key and ID pairs in a Map. Any other suggestions are very much appreciated. The codes are as below,
Note: They are minimized for brevity.
#Entity
#Table(name = "FIRE_CSV_UPLOAD",
uniqueConstraints={#UniqueConstraint(columnNames = {"account_number" , "account_type", "bank_client_id"})})
public class FireCsv {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
#Column(name="account_number")
private String accountNumber;
#NotNull
#Column(name="account_type")
private String accountType;
#NotNull
#Column(name="bank_client_id")
private String bankClientIdNumber;
...
other fields/getters/setters
}
--
public interface FireCsvRepository extends JpaRepository<FireCsv, Long> {
#Query("select u from FireCsv u where u.accountNumber = :accountNumber and u.accountType = :accountType and u.bankClientIdNumber = :bankClientIdNumber ")
FireCsv findRecord(#Param("accountNumber") String accountNumber,
#Param("accountType") String accountType,
#Param("bankClientIdNumber") String bankClientIdNumber);
}
--
#Service
public class FireCsvServiceImpl implements FireCsvService {
other fields/methods
...
#Override
#Transactional
public FireCsv save(final FireCsv fireCsv) {
FireCsv existingFireCsv = fireCsvRepository.findRecord(fireCsv.getAccountNumber(), fireCsv.getAccountType(), fireCsv.getBankClientIdNumber());
// If record exist then mark as update, if not as insert
if (existingFireCsv != null) {
fireCsv.setId(existingFireCsv.getId());
fireCsv.setRecordStatus(CSVUploadRecordStatus.CSV_UPDATE.getStatus());
}
else {
fireCsv.setRecordStatus(CSVUploadRecordStatus.CSV_INSERT.getStatus());
}
fireCsv.setRecordStatusDate(new java.sql.Timestamp(new Date().getTime()));
return fireCsvRepository.save(fireCsv);
}
}
You have to read before deciding to make an update or insert, I dont think there is a way around it.
To make that faster you should add an index to your database
using the three columns "account_number", "account_type", "bank_client_id".
Alternatively you can try to use an composite id using #IdClass as shown in
tutorial-jpa-composite-primary-key
The JPA provider should than automatically create the index for it.

Cannot override default id column spring data JPA

I recently refactored our database schema to have the id columns in each of our tables to be in the format "tableName" + ID, IE "SettingsID" instead of just "id" across all tables. Now my spring-data backend is breaking when I try to fetch anything from one of those tables. It complains that there is an "invalid column name 'id'". I assume what I need to do is map my new name of the ID column to what spring data wants to be the id column, but I havent been successful so far.
I think the only configuration needed would happen within my entity object. Here is my Settings.java entity object class:
#Entity
#Table(name = Settings.TABLE_NAME)
public class Settings extends AbstractPersistable<Long> {
public static final String TABLE_NAME = "SETTINGS";
#AttributeOverride(name = "id", column = #Column(name="settingsID"))
private long settingsID;
#Column(name = "CUSTOMERID")
private String customerID;
#Column(name = "MERCHANTID")
private String merchantID;
...
....
}
And just in case it matters, (Which I don't think it does) here is the function I am calling when this error is thrown:
#Repository
public interface SettingsDAO extends CrudRepository<Settings, Long> {
/**
* Finds the entry in the Settings table for a given customerID
* #param customerID The customerID to search for
* #return The settings object for the given customer ID
*/
Settings findOneByCustomerID(String customerID);
}
The error I get is (beside from the generic hibernate error saying it cant extract the resultset) is
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'id'.
After checking out this post, I realized that I wasn't supposed to add the #AttributeOverride annotation on the field, but the class itself. That way, spring data will not try to double map the id field. My new entity object looks like this:
#Entity
#Table(name = Settings.TABLE_NAME)
#AttributeOverride(name="id", column=#Column(name="SETTINGSID"))
public class Settings extends AbstractPersistable<Long> {
public static final String TABLE_NAME = "SETTINGS";
#Column(name = "CUSTOMERID")
private String customerID;
#Column(name = "MERCHANTID")
private String merchantID;
...
Notice there is no field for id now.
Adding this name/value pair to the application.properties worked for me without having to use the AttributeOverride annotation
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy

How to get the required fields data from an Entity to VO object using Spring Data MongoDB

In Spring Data Jpa we have a flexibility to map the required fields query result to a VO object with out exposing the entity object outside.
DB Used :: mysql
1st way ::
#Query("SELECT new CountryVO(c.id,c.name) FROM Country c")
public List<CountryVO> getCountries();
//Country Entity
public class Country{
private long id;
private String name;
#DBRef
private State state;
}
// State Entity
public class State{
private long id;
private String name;
}
//Country VO
public class CountryVO{
private long id;
private String name;
private String stateName;
}
2nd way ::
#Query("SELECT DISTINCT c.name FROM Country c")
public List<String> getNames();
Now my point is I am using Spring Data mongoDB with mongoDB database and here the querying way is different like below
#Query(value = "{}", fields = "{'id':1,'name':1,'state.name':1}")
List<CountryVO> getAllCountries();
In the above query we can mention what ever fields we want in the fields attribute and the remaining fields will come as null values, but I want to map the output result to a VO like Spring Data Jpa.
Please let me know any possibilities
Thanks in Advance
Just use your CountyVO as return type:
#Query(value = "{}", fields = "{'id':1,'name':1}")
List<CountryVO> getAllCountries();

Categories