Spring Boot Data JDBC field as json value - java

I have a model which i want to save to database.
#Data
public class Model {
#Id
private UUID id;
private String name;
private ModelSettings settings;
#Data
static class ModelSettings {
boolean fuelEngine;
}
}
create table model
(
id uuid not null,
name varchar(25) not null,
settings jsonb
)
i try to save modelSettings as jsonb object using simple repository method save(), but i got error
ERROR: relation "settings" does not exist
i wrote custom Converter and i see when modelSettings is converted to json, but after prepare statement Spring Data try to save settings field to related table. How to tell Spring Data save field as json only, not row in related table?
Sorry, i forgot #WritingConverter with JdbcValue.

Hi Please use Embedded Annotation:
Embedded entities are used to have value objects in your java data model, even if there is only one table in your database.
#Data
public class Model {
#Id
private UUID id;
private String name;
#Embedded(onEmpty = USE_NULL)
private ModelSettings settings;
#Data
static class ModelSettings {
boolean fuelEngine;
}
}

You can not have an object in your entity and expect JDBC to save it as json for you.
you need to define a String column and write a converter for it for saving in and reading from database.
also some databases like Oracle supports json values but you have not mentioned which database you are using.
in Oracle database you can define a table including a json column as below:
CREATE TABLE "USERS"
(
"ID" NUMBER(16,0) PRIMARY KEY,
"USER_NAME" VARCHAR2(85) UNIQUE NOT NULL,
"PASSWORD" VARCHAR2(48) NOT NULL,
"PROFILE" NCLOB NOT NULL CONSTRAINT profile_json CHECK ("PROFILE" IS JSON),
"SETTINGS" NCLOB NOT NULL CONSTRAINT settings_json CHECK ("SETTINGS" IS JSON),
);
And you need to create your entity class as below:
#Entity
#Table(name = "Users")
public class User {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GENERATOR")
#SequenceGenerator(name = "USER_GENERATOR", sequenceName = "USERS_SEQ", allocationSize = 1)
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#Lob
#Nationalized
#Column(name = "PROFILE",columnDefinition="NCLOB NOT NULL")
private String profile;
#Lob
#Nationalized
#Column(name = "SETTINGS",columnDefinition="NCLOB NOT NULL")
private String settings;
}
as you can see here profile and setting are my json columns.

Related

JPA/Hibernate Spring boot-primary key one entity referred as an instance to other entity not working

I have generated master tables using liquibase. I have created the corresponding models in spring boot now I want to maintain a relation ship between those models.
I have one table called Vehicle_Type, it is already pre-populated using liquibase.
#Data
#Entity
#Table(name="VEHCILE_TYPE")
public class VehicleType {
#Id
private int id;
#Column(name="DISPLAY_NAME")
private String displayName;
#Column(name="TYPE")
private String type;
#Column(name="CREATED_DATE")
private LocalDateTime createdDate;
#Column(name="UPDATED_DATE")
private LocalDateTime updateDate;
}
now what I want to achieve is, I have one child entity, I have refer the VehicleType instance inside that entity as depicted below
#Data
#Entity
#EqualsAndHashCode(callSuper = true)
#Table(name = "NON_MSIL_VEHICLE_LAYOUT")
public class NonMsilVehicleLayout extends BaseImagesAndLayout {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "NMV_SEQ")
#SequenceGenerator(sequenceName = "NON_MSIL_VEH_SEQUENCE", allocationSize = 1, name = "NMV_SEQ")
private int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "VEH_TYPE", referencedColumnName = "id")
private VehicleType vehicleType;
public interface VehType {
String getVehType();
}
}
The problem is when I tries to save entity NonMsilVehicleLayout, then it tries to first insert the data in VEHICLE_TYPE table also. which should not going to be happen.
I don't want that, I want JPA will pick the correct ID from VEHICLE_TYPE table and place it inside the corresponding table for NonMsilVehicleLayout, because the id of VEHICLE_TYPE table is act as foreign key in Non_Msil_Vehicle_Layout table.
log.info("Inside saveLayout::Start preparing entity to persist");
String resourceUri = null;
NonMsilVehicleLayout vehicleLayout = new NonMsilVehicleLayout();
VehicleType vehicleType=new VehicleType();
vehicleType.setType(modelCode);
vehicleLayout.setVehicleType(modelCode);
vehicleLayout.setFileName(FilenameUtils.removeExtension(FilenameUtils.getName(object.key())));
vehicleLayout.setS3BucketKey(object.key());
I know I missed something, but unable to figure it out.
You are creating a new VehicleType instance setting only the type field and set the vehicleType field of NonMsilVehicleLayout to that new instance. Since you specified CascadeType.ALL on NonMsilVehicleLayout#vehicleType, this means to Hibernate, that it has to persist the given VehicleType, because the instance has no primary key set.
I guess what you rather want is this code:
vehicleLayout.setVehicleType(
entitManager.createQuery("from VehicleType vt where vt.type = :type", VehicleType.class)
.setParameter("type", typeCode)
.getSingleResult()
);
This will load the VehicleType object by type and set that object on NonMsilVehicleLayout#vehicleType, which will then cause the foreign key column to be properly set to the primary key value.
Finally, after some workaround, I got the mistake, the column name attribute was incorrect, so I made it correct and remove the referencedColumn and Cascading.
Incorrect:
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "VEH_TYPE", referencedColumnName = "id")
private VehicleType vehicleType;
Correct:
#OneToOne
#JoinColumn(name = "VEHICLE_TYPE")
private VehicleType vehicleTypes;
also I have added the annotation #Column in the referende entity VehicleImage
public class VehicleType {
#Id
#Column(name = "ID") // added this one
private int id;
}
That bit workaround solved my problem, now I have achieved what I exactly looking for.

Can i get data from multiple tables without jointables or foreign keys at them

I'm trying to get a single data from two tables of database. These tables doesn't have foreign keys and no jointables too. I'm using spring-data to retrieve required data for first data set.
I have two data sets that have a common String value, and want to retrieve data from both tables not using jointables or foreign keys, retrieving data from the first data set.
I'm using simple DataRepository interface
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataRepository extends JpaRepository<FirstData, Long> {
DataService getById(Long id);
}
FirstData entity:
#Data
#Entity
#Table(schema = "someschema", name = "firstdata")
public class FirstData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "name")
private String name;
#Column(name = "type")
private String type;
}
SecondData entity:
#Data
#Entity
#Table(schema = "someschema", name = "seconddata")
public class SecondData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "type")
private String type;
#Column(name = "value")
private String value;
}
and DataService
#Service
public class DataService {
private DataRepository dataRepository;
public DataService(DataRepository dataRepository){
this.dataRepository = dataRepository;
}
public void getBothFirstAndSecondData() {
List<FirstData> firstDataSet = dataRepository.findAll();
}
}
I need to get data from both tables, but don't want to modify table structure, make jointable or add foreign keys. Also, i don't want to add another repository write code arount second data set. I need just to have a "value" from second data set at first data set result. What is the simpliest approach for solving such data retrieveing?
sql query the above problem can be:
select value from seconddata where id IN (select id from firstdata)

Modeling UUID in Hibernate entity against MySQL

I have a very similar question to this one, however my question is even more basic than that one and so I don't feel like its a dupe...(we'll see what SO thinks). And if it is a dupe of another question, please point out to me (perhaps in a comment) how the accepted answer completely answers/addresses my issue (I don't think it does!).
I have a Java/JPA/Hibernate #Entity class that needs to have a UUID field:
#Canonical
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private UUID refId;
// Getters/setters/ctors/etc.
}
#Entity(name = "accounts")
#AttributeOverrides({
#AttributeOverride(name = "id", column=#Column(name="account_id")),
#AttributeOverride(name = "refId", column=#Column(name="account_ref_id"))
})
public class Account extends BaseEntity {
// blah whatever
}
That is, I need to be working with UUIDs at the app layer. However the database here is MySQL, and the overwhelming recommendations for representing UUIDs in MySQL seems to be to represent them as a VARCHAR(36), so that's what I have.
At runtime I'm getting the following exception:
Caused by: org.hibernate.HibernateException: Wrong column type in my_db.accounts for column account_ref_id. Found: varchar, expected: binary(255)
at org.hibernate.mapping.Table.validateColumns(Table.java:373)
at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1338)
<rest of stack trace omitted for brevity>
So clearly, the database is presenting a VARCHAR(36), but Hibernate seems to be defaulting to expect a binary(255).
I honestly don't care what the UUID gets stored as in the DB. It could be VARCHAR, it could be TEXT, it could be BLOB...it could be a toaster oven for all I care! But the essential requirement here is that the data model (the entity class) use UUIDs.
What's the fix here? If I have to change the column type in the DB, what do I change the type to? If I have to change/modify the UUID field in the entity, what else do I need to annotate it with?
For me the fix required changes to both DB schema as well as the entity (Java) code.
First I changed my CREATE TABLE statement to use BINARY(255) instead of VARCHAR(36):
CREATE TABLE IF NOT EXISTS accounts (
# lots of fields...
account_ref_id BINARY(255) NOT NULL,
# ...lots of other fields
);
Next I added #Type(type="uuid-binary") to my field declaration in the entity:
#Canonical
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Type(type="uuid-binary")
private UUID refId;
// Getters/setters/ctors/etc.
}
#Entity(name = "accounts")
#AttributeOverrides({
#AttributeOverride(name = "id", column=#Column(name="account_id")),
#AttributeOverride(name = "refId", column=#Column(name="account_ref_id"))
})
public class Account extends BaseEntity {
// blah whatever
}
This works beautifully for me.
Try it like this:
public class BaseEntity{
#Column(nullable = false)
private String uuid;
public BaseEntity(){
setUuid(UUID.randomUUID().toString());
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
#PrePersist
public void prePersist(){
if(null == getUuid())
setUuid(UUID.randomUUID().toString());
}
....
Column definition:
uuid varchar(255) DEFAULT NULL
When generating UUID use toString method:
entity.setUuid(UUID.randomUUID().toString())

How to give Trigger generated value into Hibernate ValueObject?

In my Hibernate Application i'm using create a ValueObject class
#Entity
#Table(name="user")
public class UserVO{
#Id
#Column(name="S_ID")
private String s_id;
#Column(name="FIRSTNAME")
private String firstName;
#Column(name="LASTNAME")
private String lastName;
}
and in my Service class i'm writing like this
public void createOrUpdateUser(UserVO userVO) {
userDAO.createOrUpdateUser(userVO);
}
and in my DAO class i'm writing like this
private EntityManager entityManager;
public void createOrUpdateUser(UserVO userVO) throws DataAccessException {
entityManager.persist(userVO);
}
now i'm calling createOrUpdateUser(userVO) but it give error
Caused by: org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()
Actually my data base i have created one trigger for user table to generate unique id for s_id column is their any problem for trigger..please suggest me..
If you are using a trigger, the intended generation strategy is org.hibernate.id.SelectGenerator. However, in order to use this strategy, Hibernate must be able to locate the inserted row after insertion to see what value the trigger assigned there are 2 ways to do this.
First is to specifically configure the generator to tell it a column that define a unique key (at least logically) within the table:
#Id
#Column(name="S_ID")
#GeneratedValue( strategy = "trigger" )
#GenericGenerator(
name="trigger", strategy="org.hibernate.id.SelectGenerator",
parameters = {
#Parameter( name="keys", value="userName" )
}
)
private String s_id;
private String userName;
The other is via Hibernate's natural-id support:
#Id
#Column(name="S_ID")
#GeneratedValue( strategy = "trigger" )
#GenericGenerator( name="trigger", strategy="org.hibernate.id.SelectGenerator" ) )
private String s_id;
#NaturalId
private String userName;
GenerationType.IDENTITY may work for you. It will really come down to the JDBC driver and how (if) it implements getGeneratedKeys
ID column must not be null, whatever you do in database driver, will only be trigger before/after insert or any other operation, But according to error it is giving error before any error.
Set something to ID values or try something
#GeneratedValue(strategy = GenerationType.IDENTITY)
as #vinit said :
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name="S_ID")
private String s_id;
is the best choice ...

How hibernate retrieve data from existing database view?

I'm new to hibernate. My problem is that I have an Oracle database. I have a view in the database. Now I want to use hibernate to retrieve data in that view. Is there any possible solutions?
Below Snippet can solve your problem, which has been extracted from the tutorial: Mapping Hibernate Entities to Views
Database Query
CREATE OR REPLACE VIEW cameron AS
SELECT last_name AS surname
FROM author
WHERE first_name = 'Cameron';
view entity
#Entity
#NamedNativeQuery(name = "findUniqueCameronsInOrder", query = "select * from cameron order by surname", resultClass = Cameron.class)
public class Cameron implements java.io.Serializable {
private static final long serialVersionUID = 8765016103450361311L;
private String surname;
#Id
#Column(name = "SURNAME", nullable = false, length = 50)
public String getSurname() {
return surname;
}
public void setSurname(final String surname) {
this.surname = surname;
}
}
Hibernate mapping file.
<mapping class="examples.hibernate.spring.query.domain.Cameron" />
finally some test !...
#Test
public void findTheCameronsInTheView() throws Exception {
final List<Cameron> camerons = findUniqueCameronsInOrder();
assertEquals(2, camerons.size());
final Cameron judd = camerons.get(0);
final Cameron mcKenzie = camerons.get(1);
assertEquals("Judd", judd.getSurname());
assertEquals("McKenzie", mcKenzie.getSurname());
}
A view is from accessing data nothing different from table, a problem arises when you want to add,update or delete from view.
Please read http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/querysql.html
It' very similar to mapping ordinary database table.
Create an Entity and use your view name as Table name.
#Entity
#Table(name = "rc_latest_offer_details_view")
public class OfferLatestDetailsViewEntity {
#Id
#Column(name = "FK_OFFER_ID")
private int offerId;
#Column(name = "MAX_CHANGED_DTM")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime changedDateTime;
private BigDecimal price;
...
}
Then query for entities same way as you do for normal table.
Working in Hibernate 4, Spring 4.
we can achieve this by using # Immutable annotation in entity class to map database view with Hibernate
For example : I have created one database view user_data which have 2 columns (id and name) and mapped user_data view in the same way as database tables.
#Entity
#Table(name = "user_data")
#Immutable
public class UserView {
#Id
#Column(name = "ID")
private int ID ;
#Column(name = "NAME")
private String name ;
}

Categories

Resources