Cannot override default id column spring data JPA - java

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

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 extract value from "dictionary table" in JPA

I want to map two tables (ManyToOne connection) to one object in Java. One is primary CatalogObject table, the second is just a dictionary of possible types of objects. In Java I want to just have the String of type instead of mapping to a new object.
When I want to search for all objects in the class (f.e. "database") i have to first find an Id of type "database" and then find all CatalogObjects with this Id specified. Which looks a bit tedious.
CatalogObject Table:
ID, Name, Parent_ID (FK), TYPE_ID (FK)
Type Table:
ID, Type.
I've created a mapping with CatalogObject and CatalogObjectType classes, but CatalogObjectType class holds only single String right now.
public class CatalogObject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
CatalogObject parent;
String name;
#ManyToOne
#JoinColumn(name = "type_id")
CatalogObjectType type;
}
public class CatalogObjectType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
}
I want to replace CatalogObjectType with just a String value of the associated type. How to configure it for Hibernate/JPA? Can it be done?
What I want is:
public class CatalogObject implements Serializable {
...
String name;
#SomeAnnotation(name = "type_id")
String type;
}
Yes, you can map an entity to 2 database tables in 2 simple steps:
You need to annotate your entity with JPA’s #Table and #SecondaryTable annotations and provide the names of the first and second table as the value of the name parameters.
You need to annotate each attribute which you want to map to the secondary table with a #Column annotation and set the name of the secondary table as the value of the table attribute.
The #Table annotation defines the primary table to which the entity attributes get mapped by default.
The #SecondaryTable annotation specifies the second database table to which the entity gets mapped.
That’s all you need to do to map the 2 database tables to the one entity.
You can check this link for a detailed explanation with a sample.

Map one value to multiple columns in JPA

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;

Hibernate Criteria select using embedded object (tuple)

In my case I have a SQL query which looks like:
select * from event_instance where (object_id, object_type) in
(<LIST OF TUPLES RETRIEVED FROM SUBQUERY>);
I want to map this on Hibernate Entities and I have a problem with this query. My mapping looks like that:
#Entity
#Table(name="event_instance")
public class AuditEvent {
<OTHER_FIELDS>
#Column( name = "object_type", nullable = false)
private String objectType;
#Column( name ="object_id" , nullable = false)
private Integer objectId;
}
and second entity:
#Entity
#Table(schema = "els" ,name = "acg_objects")
public class AcgObject implements Serializable{
#Id
#Column(name = "acg_id")
private String acgId;
#Id
#Column(name="object_type")
private String objectType;
#Id
#Column(name="object_id")
private Integer objectId;
<OTHER FIELDS>
}
I already run query for getting AcgObjects and for my DAO I'm getting List only thing I want to do is query a touple using criteria like:
crit.add(Restrictions.in("objectType,objectId",<List of tuples>);
Is it possible? I was trying to use #Embedded object but don't know how exactly construct a query for it. Please help
You can do that not in standard SQL nor using criteria; you have to split in two distinct restrictions or using a Session.SQLQuery() if you want to use specific RDBMS (look at SQL WHERE.. IN clause multiple columns for an explanation)

Hibernation annotations, specify column default value

I have a domain object and annotated as follows
#Entity
#Table(name = "REQUEST")
public class Request {
/**
* Unique id for this request
*/
#Id
#GeneratedValue
#Column(name = "EQ_ID")
private long requestId;
/**
*
*/
#Column(name = "EMAIL_ID")
private String emailId;
/**
*
*/
#Column(name = "REQUEST_DATE")
private Date requestDate;
/**
*Getters/setters omitted
*/
}
The column Request_date cannot be null and as per the DDL the default value is sysdate (oracle DB). How do I annotate this field so that if the requestDate property is null,hiberanate automatically inserts sysdate.? Currently it throws error when the field is null,which is very obvious as it cannot be null as per the DB constraints. How do I go about this?
One alternative is to mark this field as transient and the inserts work fine. But the negative aspect is that, I will not be able to retrieve the value (of request_date column).
This is a missing feature in hibernate annotations. Also there exist some workaround as Yok has posted. The problem is that the workaround is vendor dependent and might not work for all DB. In my case,Oracle, it isn't working and has been reported as a bug.
You can put the default value in a columnDefinition. An example would look like:
#Column(name = "REQUEST_DATE", nullable = false, columnDefinition = "date default sysdate")
Using #ColumnDefault (Work for DDL update).
hibernate.hbm2ddl.auto=update
import org.hibernate.annotations.ColumnDefault;
....
#ColumnDefault(value="'#'")
#Column(name = "TEMP_COLUMN", nullable = false)
public String getTempColumn() {
return tempColumn;
}
DDL Generate:
Alter Table YOUR_TABLE add TEMP_COLUMN varchar2(255) default '#' not null;
Assign a default value to the field:
private Date requestDate = new Date();
If you mark your entity with #DynamicInsert e.g.
#Entity
#DynamicInsert
#Table(name = "TABLE_NAME")
public class ClassName implements Serializable {
Hibernate will generate SQL without null values. Then the database will insert its own default value. This does have performance implications See Dynamic Insert.
Make the default in Oracle for the column SYSDATE:
ALTER TABLE APP MODIFY (REQUEST_DATE DEFAULT SYSDATE);
Then, from Hibernate's perspective it can be nullable.
Hibernate will save a NULL to the database. Oracle will convert that to SYSDATE. And everyone will be happy.
I resolved assigning a value to the variable like this private Integer active= 0;
#Entity
#Table(name="products")
public class ServiziTipologia {
#Id
#GeneratedValue
private Integer id;
private String product;
private String description;
private Integer active= 0;

Categories