IDENTITY_INSERT with JPA - java

I am attempting to insert some fake data into an SQL Server Express database. I am using some simple code like this:
#Entity
#Table(name = "People")
public class Peeps implements Serializable {
#Id
#Column(name = "ID", columnDefinition = "Decimal(10,0)")
private String id;
#Column(name = "PERSON_NAME")
private String name;
}
I call the entity manager to create the above class as follows:
private EntityManager em = createManager();
private EntityTransaction utx = em.getTransaction();
final Peeps entity = new Peeps();
entity.setId("10002");
entity.setName("Joe");
utx.begin();
em.persist(entity);
utx.commit();
However, when doing this, I get an error::
Cannot insert explicit value for identity column in table 'People' when IDENTITY_INSERT is set to OFF
So what I tried doing is something like this:
em.createNativeQuery("SET IDENTITY_INSERT People ON").executeUpdate();
em.persist(entity);
em.createNativeQuery("SET IDENTITY_INSERT People OFF").executeUpdate();
However, I still get the same error. My intuition leads me to believe that the connection is not being shared. Is there something I can do to instruct hibernate to set IDENTITY_INSERT to ON before I invoke my persistence?

You are missing the next line under your id field:
#GeneratedValue(strategy=GenerationType.IDENTITY)
So your id field should look like this:
#Entity
#Table(name = "People")
public class Peeps implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "ID", columnDefinition = "Decimal(10,0)")
private String id;
#Column(name = "PERSON_NAME")
private String name;
}
Second, you are not supposed to set an ID to your entity by yourself, the ID will be generated automatically when you'll persist it using hibernate.
so get rid of: entity.setId("10002");
and just do this:
private EntityManager em = createManager();
private EntityTransaction utx = em.getTransaction();
final Peeps entity = new Peeps();
entity.setName("Joe");
utx.begin();
em.persist(entity);
utx.commit();
something else make sure that you configured your primary key in your DB table, with auto increment.

Related

How to ignore a #SQLDelete annotation in certain cases

I want to implement soft deletion, but still be able to delete permanently. Is there any way to ignore a declared #SQLDelete() annotation, or maybe say:
#SQLDelete("IF expression THEN UPDATE statement ELSE delete statement")
EDIT:
This is the entity in question.
#Indexed
#Entity
#DynamicUpdate
#FilterDefs({
#FilterDef(name = "tenantFilter", parameters = {#ParamDef(name = "tenantId", type = "string")}),
#FilterDef(name = "deleteFilter")
})
#Filters({
#Filter(name = "tenantFilter", condition = "tenant_id = :tenantId"),
#Filter(name = "deleteFilter", condition = "deleted = false")
})
#SQLDelete(sql ="UPDATE Antwort SET deleted = true, date_modified = NOW() WHERE ID = ?; DELETE FROM Antwort WHERE deleted = true AND kundenentwurf_id IS NOT NULL", check = ResultCheckStyle.NONE)
public class Antwort implements TenantSupport, ISoftDeleteModel{
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String tenantId;
#Column(nullable = false)
private Boolean deleted;
private Date dateModified;
#ManyToOne(fetch = FetchType.LAZY)
private Organisation organisation;
#ManyToOne(fetch = FetchType.LAZY)
private Projekt projekt;
#ManyToOne(fetch = FetchType.LAZY)
private Kundenentwurf kundenentwurf;
private Integer nummer;
private String frage;
private String antwort;
//getters, setters and contructors...
}
I explained this in more details in one of my Hibernate Tips. Here is the short version of it:
When you call the remove method on your EntityManager, Hibernate will execute the SQL statement defined in the #SQLDelete operation.
You can’t deactivate the #SQLDelete annotation. So, if you want to remove the record from the database permanently, you can’t use the remove method of your EntityManager. You need to execute a SQL DELETE statement using a JPQL, Criteria or native query.
Here is an example of a JPQL DELETE statement:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// do something ...
// add this if you fetched the Book entity in this session
em.flush();
em.clear();
Query query = em.createQuery("DELETE Book b WHERE id = :id");
query.setParameter("id", 1L);
query.executeUpdate();
em.getTransaction().commit();
em.close();
In case you fetched the Book entity you want to remove within your current Hibernate Session, you need to call the flush and clear methods on your EntityManager before you execute the DELETE statement. This ensures that all pending changes are written to the database before you remove the record.

Adding an object from Object A into Object B without creating new object ? HIBERNATE

Lets say I have two objects, say one is a User object and the other is a State Object. The state object is basically the 50 states of America so it doesn't ever have to change. The user object however has a Collection of States where the user has been. So like this:
#Entity
#Table(name="tbl_users")
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", unique=true, nullable = false)
private int id;
#Column(name="user_name")
private String name;
#OneToMany(targetEntity=State.class, orphanRemoval = false)
#Column(name="states")
private Collection<State> states;
//getters and setters
}
and the States entity looks like this:
#Entity
#Table(name="tbl_states")
class State {
#Id
#Column(name="id", unique=true, nullable=false)
private int id;
#Column(name="state")
private String state;
// getters and setters
}
Code for adding user (using hibernate):
public int addUser(User user) {
em.persist(user);
em.flush();
return user.getId();
}
Code for getting state by id:
public State getStateById(int id) {
return em.createQuery("SELECT s FROM State s WHERE s.id =:id, State.class)
.setParameter("id", id)
.getSingleResult();
}
but when I try to create a User and pick several states, I get a PSQLException:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "uk_g6pr701i2pcq7400xrlb0hns"
2017-06-21T22:54:35.959991+00:00 app[web.1]: Detail: Key (states_id)=(5) already exists.
I tried looking up the Cascade methods to see if I could use any, but Cascade.MERGE and Cascade.PERSIST seem to do the same thing, and the rest I don't think I need (REMOVE, DETACH, etc). My question is:
How do I add states to the User object without having that error?
This code works:
class Example {
#Test
public void workingTest() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testPU");
EntityManager em = emf.createEntityManager();
// Creating three states
State alabama = new State(state: 'Alabama');
State louisiana = new State(state: 'Louisiana');
State texas = new State(state: 'Texas');
em.getTransaction().begin();
em.persist(alabama);
em.persist(louisiana);
em.persist(texas);
em.getTransaction().commit();
List<State> states = em.createQuery('FROM State').getResultList();
// Assert only three states on DB
assert states.size() == 3;
User userFromAlabama = new User();
User userFromAlabamaAndTexas = new User();
em.getTransaction().begin();
State alabamaFromDB = em.find(State, alabama.getId());
State texasFromDB = em.find(State, texas.getId());
userFromAlabama.getStates().add(alabamaFromDB);
userFromAlabamaAndTexas.getStates().add(alabamaFromDB);
userFromAlabamaAndTexas.getStates().add(texasFromDB);
em.persist(userFromAlabama);
em.persist(userFromAlabamaAndTexas);
em.getTransaction().commit();
states = em.createQuery('FROM State').getResultList();
// Assert only three states on DB again
assert states.size() == 3;
// Assert one user
User userFromDB = em.find(User, userFromAlabama.getId());
assert userFromDB.getStates().size() == 1;
userFromDB = em.find(User, userFromAlabamaAndTexas.getId());
assert userFromDB.getStates().size() == 2;
}
}
#Entity
#Table(name="tbl_users")
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id
#Column(name="user_name")
private String name
#ManyToMany
private Collection<State> states = Lists.newArrayList()
// Getters and setters
}
#Entity
#Table(name="tbl_states")
class State {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="state")
private String state;
// Getters and setters
}
You should change your mapping to #ManyToMany!
And you must have 3 tables on DB like this:
TBL_USERS, TBL_STATES and TBL_USERS_TBL_STATES
The TBL_USERS_TBL_STATES table is the default table name that Hibernate uses when a property is annotated with #ManyToMany. If you want to change the tablename of TBL_USERS_TBL_STATES, use the #JoinTable annotation too. See the docs here
With this configuration, you should be able to fetch a State from database, add it to a new User and then persist it. I made a unit test and It works!
In your case it might be better to use a manytomany association with manytomany hibernate dont generate unicity constraint.
Hibernate auto generation scheme behavior is a little bit strange with onetoMany but you can use this workaround.
Try this:
#ManyToMany
#JoinTable(name = "user_state")
private List<State> states;

JPA Join table with SELECT

I have a SQL query like this:
SELECT h.name, h.created_date, tbl.*
FROM my_table tbl
LEFT JOIN
(SELECT name, max(created_date) created_date FROM my_table GROUP BY name) h
ON tbl.name = h.name;
It returns the row from my_table (which has multiple for name="") along with the maximum created_date for that name.
Is there a way to replicate this in a JPQL query?
Here is the gist of the Entity class, it's quite simple:
#Entity
#Table(name = "MY_TABLE")
#XmlRootElement
public class MyTable implements Serializable {
private BigDecimal tableId;
private String name;
private Date createdDate;
// ...
#Id
#Basic(optional = false)
#Column(name = "TABLE_ID")
#GeneratedValue(generator = "TBL_ID_SEQ")
public BigDecimal getTableId() {
return tableId;
}
#Basic(optional = false)
#Column(name = "NAME")
public String getName() {
return name;
}
#Basic(optional = false)
#Column(name = "CREATED_DATE", insertable = false)
#Temporal(TemporalType.TIMESTAMP)
public Date getCreatedDate() {
return createdDate;
}
// ... getters/setters
}
Just reading your question I guess you do not need another entity. Entities in JPA are the same like tables in SQL. Usually there is a 1:1 relationship between entities and tables. You just have to know how to invoke a query using JPQ. You need a entity manager, which invokes your statement.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnit");
EntityManager em = emf.createEntityManager();
You have to define your persistence unit, i.e. in a pom file, or a config java file. So done you can go on coding something like this:
Query q = em.createQuery( "Your query in sql syntax as a string object" );
In respect to your entities and invoked query you will receive a List using
List<object> resultOfMyQuery = q.getResultList();
This is only one short example. But hopefully you got some buzzwords to look for ;)

JPA Join using arbitrary field (not primary key)

I've got two entities that I want to join together using a field they have in common, called shared_id. The field is not the primary key of either entity. The shared_id is unique - each Hipster will have a unique shared_id.
The tables look like:
Hipster Fixie
========= ========
id id
shared_id shared_id
There is a OneToMany relationship between Hipsters and their Fixies. I've tried something like this:
#Entity
public class Hipster {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "shared_id")
private Integer sharedId;
#OneToMany(mappedBy = "hipster")
private List<Fixie> fixies;
}
#Entity
public class Fixie {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name = "shared_id", referencedColumnName = "shared_id")
private Hipster hipster;
}
#Repository
public class HipsterDAO {
#PersistenceContext
private EntityManager entityManager;
public Hipster getHipsterBySharedId(Integer sharedId) {
String queryString = "SELECT h FROM Hipster h WHERE h.sharedId = :sharedId";
TypedQuery<Hipster> query = entityManager.createQuery(queryString, Hipster.class);
query.setParameter("sharedId", sharedId);
try {
return query.getSingleResult();
} catch (PersistenceException e) {
return null;
}
}
}
Now, my DAO gives me this error:
java.lang.IllegalArgumentException: Can not set java.lang.Integer field Hipster.sharedId to java.lang.Integer
I think it's upset because the sharedId field is used in a relation, rather than just being a basic field. I haven't included the sharedId field in the Fixie entity, but I get the same result if I do. How do I persuade it to run this query for me? Do I need to change the query or the entities?

OneToOne between two tables with shared primary key

I'm trying to set up the following tables using JPA/Hibernate:
User:
userid - PK
name
Validation:
userid - PK, FK(user)
code
There may be many users and every user may have max one validation code or none.
Here's my classes:
public class User
{
#Id
#Column(name = "userid")
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long userId;
#Column(name = "name", length = 50, unique = true, nullable = false)
protected String name;
...
}
public class Validation
{
#Id
#Column(name = "userid")
protected Long userId;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid")
protected User user;
#Column(name = "code", length = 10, unique = true, nullable = false)
protected String code;
...
public void setUser(User user)
{
this.user = user;
this.userId = user.getUserId();
}
...
}
I create a user and then try to add a validation code using the following code:
public void addValidationCode(Long userId)
{
EntityManager em = createEntityManager();
EntityTransaction tx = em.getTransaction();
try
{
tx.begin();
// Fetch the user
User user = retrieveUserByID(userId);
Validation validation = new Validation();
validation.setUser(user);
em.persist(validation);
tx.commit();
}
...
}
When I try to run it I get a org.hibernate.PersistentObjectException: detached entity passed to persist: User
I have also tried to use the following code in my Validation class:
public void setUserId(Long userId)
{
this.userId = userId;
}
and when I create a validation code I simply do:
Validation validation = new Validation();
validation.setUserId(userId);
em.persist(validation);
tx.commit();
But then since User is null I get org.hibernate.PropertyValueException: not-null property references a null or transient value: User.code
Would appreciate any help regarding how to best solve this issue!
I have been able to solve this problem of "OneToOne between two tables with shared primary key" in pure JPA 2.0 way(Thanks to many existing threads on SOF). In fact there are two ways in JPA to handle this. I have used eclipselink as JPA provider and MySql as database. To highlight once again no proprietary eclipselink classes have been used here.
First approach is to use AUTO generation type strategy on the Parent Entity's Identifier field.
Parent Entity must contain the Child Entity Type member in OneToOne relationship(cascade type PERSIST and mappedBy = Parent Entity Type member of Child Entity)
#Entity
#Table(name = "USER_LOGIN")
public class UserLogin implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="USER_ID")
private Integer userId;
#OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin")
private UserDetail userDetail;
// getters & setters
}
Child Entity must not contain an identifier field. It must contain a member of Parent Entity Type with Id, OneToOne and JoinColumn annotations. JoinColumn must specify the ID field name of the DB table.
#Entity
#Table(name = "USER_DETAIL")
public class UserDetail implements Serializable {
#Id
#OneToOne
#JoinColumn(name="USER_ID")
private UserLogin userLogin;
// getters & setters
}
Above approach internally uses a default DB table named SEQUENCE for assigning the values to the identifier field. If not already present, This table needs to be created as below.
DROP TABLE TEST.SEQUENCE ;
CREATE TABLE TEST.SEQUENCE (SEQ_NAME VARCHAR(50), SEQ_COUNT DECIMAL(15));
INSERT INTO TEST.SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0);
Second approach is to use customized TABLE generation type strategy and TableGenerator annotation on the Parent Entity's Identifier field.
Except above change in identifier field everything else remains unchanged in Parent Entity.
#Entity
#Table(name = "USER_LOGIN")
public class UserLogin implements Serializable {
#Id
#TableGenerator(name="tablegenerator", table = "APP_SEQ_STORE", pkColumnName = "APP_SEQ_NAME", pkColumnValue = "USER_LOGIN.USER_ID", valueColumnName = "APP_SEQ_VALUE", initialValue = 1, allocationSize = 1 )
#GeneratedValue(strategy = GenerationType.TABLE, generator = "tablegenerator")
#Column(name="USER_ID")
private Integer userId;
#OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin")
private UserDetail userDetail;
// getters & setters
}
There is no change in Child Entity. It remains same as in the first approach.
This table generator approach internally uses a DB table APP_SEQ_STORE for assigning the values to the identifier field. This table needs to be created as below.
DROP TABLE TEST.APP_SEQ_STORE;
CREATE TABLE TEST.APP_SEQ_STORE
(
APP_SEQ_NAME VARCHAR(255) NOT NULL,
APP_SEQ_VALUE BIGINT NOT NULL,
PRIMARY KEY(APP_SEQ_NAME)
);
INSERT INTO TEST.APP_SEQ_STORE VALUES ('USER_LOGIN.USER_ID', 0);
If you use Hibernate you can also use
public class Validation {
private Long validationId;
private User user;
#Id
#GeneratedValue(generator="SharedPrimaryKeyGenerator")
#GenericGenerator(name="SharedPrimaryKeyGenerator",strategy="foreign",parameters = #Parameter(name="property", value="user"))
#Column(name = "VALIDATION_ID", unique = true, nullable = false)
public Long getValidationId(){
return validationId;
}
#OneToOne
#PrimaryKeyJoinColumn
public User getUser() {
return user;
}
}
Hibernate will make sure that the ID of Validation will be the same as the ID of the User entity set.
Are you using JPA or JPA 2.0 ?
If Validation PK is a FK to User, then you do not need the Long userId attribute in validation class, but instead do the #Id annotation alone. It would be:
Public class Validation
{
#Id
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid")
protected User user;
#Column(name = "code", length = 10, unique = true, nullable = false)
protected String code;
...
public void setUser(User user)
{
this.user = user;
this.userId = user.getUserId();
}
...
}
Try with it and tell us your results.
You need to set both userId and user.
If you set just the user, then the id for Validation is 0 and is deemed detached. If you set just the userId, then you need to make the user property nullable, which doesn't make sense here.
To be safe, you can probably set them both in one method call:
#Transient
public void setUserAndId(User user){
this.userId = user.getId();
this.user = user;
}
I marked the method #Transient so that Hibernate will ignore it. Also, so you can still have setUser and setUserId work as expected with out any "side effects."

Categories

Resources