How to give Trigger generated value into Hibernate ValueObject? - java

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 ...

Related

Spring Boot Data JDBC field as json value

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.

How to stop Hibernate from eagerly fetching a relationship when it is mapped using a column (referencedColumnName) different than the primary key?

I'm mapping a relationship that does not use the entity's primary key. Using "referencedColumnName" with a column different than the primary key causes hibernate to eagerly fetch the association, by issuing an extra select, even when it's tagged with FetchType.LAZY.
My goal is to make it behave like a regular mapping, meaning it wouldn't issue an extra query every time I need to query the main entity.
I have already tried using #LazyToOne(LazyToOneOption.NO_PROXY), which sorts out the problem, but it does not operate well with Jackson's (JSON parsing library) module "jackson-datatype-hibernate5", which skips hibernate lazy proxies when serializing the results.
Here is a scenario almost like the one I have that causes the problem:
Entities:
#Entity(name = "Book")
#Table(name = "book")
public class Book
implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters omitted for brevity
}
#Entity(name = "Publication")
#Table(name = "publication")
public class Publication {
#Id
#GeneratedValue
private Long id;
private String publisher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "isbn",
referencedColumnName = "isbn"
)
private Book book;
#Column(
name = "price_in_cents",
nullable = false
)
private Integer priceCents;
private String currency;
//Getters and setters omitted for brevity
}
Repository (Spring-Data, but you could try directly with the EntityManager):
#Repository
public interface PublicationRepository extends JpaReadRepository <Publication, Long>
{
#Query ("SELECT d FROM Publication d WHERE d.publisher = ?1 ")
Optional <Publication> findByPublisher (String isbn);
}
Thanks
The only way to achieve what you are looking for is by moving the annotatation #Id to the isbn property.
You can leave the #GeneratedValue on the autoincrement property.
Notes:
1 - Make sure that your equals/hc are following the OID(Object ID) on your domain case the "NaturalId" ISBN.
2 - It will be good to ensure if possible on DB level that your natural ID has unique contraint on it.

Cascaded delete does not work in a Java program using Hibernate (random behavior)!

I have the following problem, which is not easy to explain me. Therefore, I will show you my java code:
Usuarios class:
#Entity
#Table( name="usuarios" )
public class Usuario implements Serializable {
private static final long serialVersionUID = 3L;
#Id
#Column( name="userid", length=8 )
private String userId;
#Column
private String nombre;
#Column
private String apellido;
#Column(name="nro_fun")
private int nroFuncionario;
#OneToMany( cascade=CascadeType.ALL, mappedBy="usuario" )
private List<UsuarioPermisos> permisos;
UsuarioPermisos class:
#Entity
#Table( name="usuarios_permisos" )
public class UsuarioPermisos {
#TableGenerator( name="claveUsuPerm",
table="numerador",
pkColumnName="clave",
valueColumnName="ultimo_numero",
pkColumnValue="ID_USU_PERMISO",
allocationSize=1
)
#Id
#Column
#GeneratedValue(strategy= GenerationType.TABLE, generator="claveUsuPerm")
private int clave;
#Column(name="userid", length=8 )
private String usuario;
#OneToOne
#JoinColumn( name="perId" )
Permisos permiso;
Then, in a servlet, implement the following:
sessionFactory = (SessionFactory) getServletContext().getAttribute( "SESSION_FACTORY" );
session = sessionFactory.openSession();
trn = session.beginTransaction();
Usuario usu = session.get( Usuario.class, varUsrId );
if( usu != null )
session.delete( usu );
The problem is the following:
varUsrId variable is loaded with the value entered by a user from a web form.
if when I run the function get the variable varUsrId has exactly 8 characters (the length of the field in the database table), the delete works well, ie, first remove all objects UsuarioPermisos class, and then removes the Usuario object (from the respective tables in the DB).
But if the variable varUsrId is less than 8 characters (for example, has the value "plopez"), the cascaded delete does not work. That is, does not eliminate UsuarioPermisos objects, however, eliminates the Usuario object. This causes an error in the database due to existing foreign key.
Does anyone have any idea what could be the problem?
These are the versions I'm using:
hibernate 5.0.6,
java 1.8.0_51,
apache-tomcat-8.0.24,
postgres 9.3.9
As you are trying to map the two entities with a OneToMany relationship, try changing the following line:
#Column(name="userid", length=8 )
private String usuario;
to:
#ManyToOne()
#JoinColumn(name="userid")
private Usuario usuario;

#GeneratedValue in Oracle with no Sequence

I have created a trigger so that my entities ids are autogenerated with a sequence each time they're inserted into my Oracle database.
The problem comes with annotating these entities for Hibernate/JPA: I need to define a #GeneratedValue annotation but I don't want to specify the sequence name -- doing that will make Hibernate query the sequence first, then insert, which is a work that is already done by the trigger.
Is there any way to skip this sequence in the #GeneratedValue with the scenario I've proposed?
Exception I get if id is not provided:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): Pattern
Pattern class:
#Entity
#Table(name = "PATTERN")
public class Patron extends HistoricoAbstractEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_PATTERN")
private Integer idPattern;
#Column
private String description;
#Column(name = "NEGATIVE")
private Boolean isNegative;
#Column(name = "type")
private Integer type;
#Column(name = "N_DAYS")
private Integer numDays;
... (getters & setters)
}
From what your code,
What I can tell you is that its not related to #GeneratedValue, it specifies that the hibernate takes responsibility to generate and idetifier for your entity. In your case your are generating id your self, so you have to manually set the id for that particular entity. Then you won't get this error any more, the other thing that you can try is use of #PrePersist annotate a method with this and try assigning a value to id in it. I haven't tried this but this should work according to this answer on SO.
Assign Custom Identifier
If your id is being generated by the database then you should use #GeneratedValue(strategy=GenerationType.AUTO) on your id field along with your #Id annotation.

JPA/Hibernate - shared primary key

I wanted to create bidirectional one to one relationship with shared primary key.
As it is stated here JPA Hibernate One-to-One relationship I have:
#Entity
public class UserProfileInformation {
#Id
#GeneratedValue(generator = "customForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "customForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "userEntity")
)
long id;
private long itemsPerPage;
#OneToOne(mappedBy="userProfileInformation")
private UserEntity userEntity;
...}
#Entity
#Table(name = "UserTable")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String publicName;
private String password;
private String emailAddress;
private String name;
private boolean active;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserProfileInformation userProfileInformation;
...}
now, when I try to persist my user in the database, I am getting org.hibernate.id.IdentifierGenerationException: null id generated for:class pl.meble.taboret.model.UserProfileInformation. Is it because, when userProfileInformation is persisted to the database userEntity doesn't have id generated at that point?
Also, what can I do to create bidirectional relationship with shared primary key in my example?
EDIT:
Requested code, this is simple controller to test the operation of persisting UserEntity.
#Controller
#RequestMapping("/test")
public class TestController {
#Autowired
UserDao userDao;
#RequestMapping(method= RequestMethod.GET)
public String t(Model model){
UserEntity entity=new UserEntity();
entity.setActive(false);
entity.setEmailAddress("a");
entity.setName("name");
entity.setPassword("qqq");
entity.setPublicName("p");
UserProfileInformation p = new UserProfileInformation(entity);
entity.setUserProfileInformation(p);
userDao.addUser(entity);
return "login";
}
}
I think the problem is with the id generation strategy. For hibernate #GeneratedValue(strategy = GenerationType.AUTO) translates into a native identifier generation. This means that hibernate expects an identity id field for the UserTable.
I don't know exactly how SQLite works in terms of identity columns, but it seems from this SO question is a little different (see the second answer).
Anyway if you plan to run your application on multiple databases is better for portability to change the id generation strategy from GenerationType.AUTO and use hibernate enhanced generators: SequenceStyleGenerator or TableGenerator. See this link in the hibernate documentation.
EDIT:
I tried to reproduce your problem, and it seems that SQLite dialect is not among the officially supported hibernate dialects. Meanwhile I tested your case with the H2 embeded database and it works as expected: your mappings are correct.
If you are using an unofficial SQLite dialect it might be a bug with this dialect.

Categories

Resources