hibernate - select/load only field - java

I want to add a field in a Hibernate table-mapped/entity class.
I want this field to not be mapped to an actual table column, and I want Hibernate not to try to insert/update it to the DB.
But I want to be able to load this field via a custom select in the DAO e.g. via
query.addEntity(getPersistentClass().getName());
The closest I got to this was by making the field #Transient,
but then even the select does not load its value. So this is not
quite what I need.
Is this possible at all and if so how?

Well if i understand what you are trying to do well then i think the solution like this
#Column(name = "{name of column}", updatable = false)
In this way the hibernate will not try to update this column once the object created

Your getter must be a bit smarter.
For exemple you can the HibernateCallback interface from spring like that:
public String getName(Session session) {
return new HibernateCallback<String>() {
#Override
public String doInHibernate(Session session) throws HibernateException {
return session.createSQLQuery("SELECT NAME FROM MY_TABLE WHERE SOME_CONDITIONS").uniqueResult();
}
}.doInHibernate(session);
}
A better way would be to create a kind of execute method in another class where you have access to the session.
With that solution you can still mark your field as #Transient.

You can use
#Column(name = "{name of column}", insertable=false, updatable = false)
Do not mark the field as #Transient.
This way this property will not be inserted or updated but can be used in selects.

Related

org.springframework.orm.jpa.JpaSystemException: identifier of an instance of com.cc.domain.User was altered from 90 to null; [duplicate]

org.hibernate.HibernateException: identifier of an instance
of org.cometd.hibernate.User altered from 12 to 3
in fact, my user table is really must dynamically change its value, my Java app is multithreaded.
Any ideas how to fix it?
Are you changing the primary key value of a User object somewhere? You shouldn't do that. Check that your mapping for the primary key is correct.
What does your mapping XML file or mapping annotations look like?
You must detach your entity from session before modifying its ID fields
In my case, the PK Field in hbm.xml was of type "integer" but in bean code it was long.
In my case getters and setter names were different from Variable name.
private Long stockId;
public Long getStockID() {
return stockId;
}
public void setStockID(Long stockID) {
this.stockId = stockID;
}
where it should be
public Long getStockId() {
return stockId;
}
public void setStockId(Long stockID) {
this.stockId = stockID;
}
In my case, I solved it changing the #Id field type from long to Long.
In my particular case, this was caused by a method in my service implementation that needed the spring #Transactional(readOnly = true) annotation. Once I added that, the issue was resolved. Unusual though, it was just a select statement.
Make sure you aren't trying to use the same User object more than once while changing the ID. In other words, if you were doing something in a batch type operation:
User user = new User(); // Using the same one over and over, won't work
List<Customer> customers = fetchCustomersFromSomeService();
for(Customer customer : customers) {
// User user = new User(); <-- This would work, you get a new one each time
user.setId(customer.getId());
user.setName(customer.getName());
saveUserToDB(user);
}
In my case, a template had a typo so instead of checking for equivalency (==) it was using an assignment equals (=).
So I changed the template logic from:
if (user1.id = user2.id) ...
to
if (user1.id == user2.id) ...
and now everything is fine. So, check your views as well!
It is a problem in your update method. Just instance new User before you save changes and you will be fine. If you use mapping between DTO and Entity class, than do this before mapping.
I had this error also. I had User Object, trying to change his Location, Location was FK in User table. I solved this problem with
#Transactional
public void update(User input) throws Exception {
User userDB = userRepository.findById(input.getUserId()).orElse(null);
userDB.setLocation(new Location());
userMapper.updateEntityFromDto(input, userDB);
User user= userRepository.save(userDB);
}
Also ran into this error message, but the root cause was of a different flavor from those referenced in the other answers here.
Generic answer:
Make sure that once hibernate loads an entity, no code changes the primary key value in that object in any way. When hibernate flushes all changes back to the database, it throws this exception because the primary key changed. If you don't do it explicitly, look for places where this may happen unintentionally, perhaps on related entities that only have LAZY loading configured.
In my case, I am using a mapping framework (MapStruct) to update an entity. In the process, also other referenced entities were being updates as mapping frameworks tend to do that by default. I was later replacing the original entity with new one (in DB terms, changed the value of the foreign key to reference a different row in the related table), the primary key of the previously-referenced entity was already updated, and hibernate attempted to persist this update on flush.
I was facing this issue, too.
The target table is a relation table, wiring two IDs from different tables. I have a UNIQUE constraint on the value combination, replacing the PK.
When updating one of the values of a tuple, this error occured.
This is how the table looks like (MySQL):
CREATE TABLE my_relation_table (
mrt_left_id BIGINT NOT NULL,
mrt_right_id BIGINT NOT NULL,
UNIQUE KEY uix_my_relation_table (mrt_left_id, mrt_right_id),
FOREIGN KEY (mrt_left_id)
REFERENCES left_table(lef_id),
FOREIGN KEY (mrt_right_id)
REFERENCES right_table(rig_id)
);
The Entity class for the RelationWithUnique entity looks basically like this:
#Entity
#IdClass(RelationWithUnique.class)
#Table(name = "my_relation_table")
public class RelationWithUnique implements Serializable {
...
#Id
#ManyToOne
#JoinColumn(name = "mrt_left_id", referencedColumnName = "left_table.lef_id")
private LeftTableEntity leftId;
#Id
#ManyToOne
#JoinColumn(name = "mrt_right_id", referencedColumnName = "right_table.rig_id")
private RightTableEntity rightId;
...
I fixed it by
// usually, we need to detach the object as we are updating the PK
// (rightId being part of the UNIQUE constraint) => PK
// but this would produce a duplicate entry,
// therefore, we simply delete the old tuple and add the new one
final RelationWithUnique newRelation = new RelationWithUnique();
newRelation.setLeftId(oldRelation.getLeftId());
newRelation.setRightId(rightId); // here, the value is updated actually
entityManager.remove(oldRelation);
entityManager.persist(newRelation);
Thanks a lot for the hint of the PK, I just missed it.
Problem can be also in different types of object's PK ("User" in your case) and type you ask hibernate to get session.get(type, id);.
In my case error was identifier of an instance of <skipped> was altered from 16 to 32.
Object's PK type was Integer, hibernate was asked for Long type.
In my case it was because the property was long on object but int in the mapping xml, this exception should be clearer
If you are using Spring MVC or Spring Boot try to avoid:
#ModelAttribute("user") in one controoler, and in other controller
model.addAttribute("user", userRepository.findOne(someId);
This situation can produce such error.
This is an old question, but I'm going to add the fix for my particular issue (Spring Boot, JPA using Hibernate, SQL Server 2014) since it doesn't exactly match the other answers included here:
I had a foreign key, e.g. my_id = '12345', but the value in the referenced column was my_id = '12345 '. It had an extra space at the end which hibernate didn't like. I removed the space, fixed the part of my code that was allowing this extra space, and everything works fine.
Faced the same Issue.
I had an assosciation between 2 beans. In bean A I had defined the variable type as Integer and in bean B I had defined the same variable as Long.
I changed both of them to Integer. This solved my issue.
I solve this by instancing a new instance of depending Object. For an example
instanceA.setInstanceB(new InstanceB());
instanceA.setInstanceB(YOUR NEW VALUE);
In my case I had a primary key in the database that had an accent, but in other table its foreign key didn't have. For some reason, MySQL allowed this.
It looks like you have changed identifier of an instance
of org.cometd.hibernate.User object menaged by JPA entity context.
In this case create the new User entity object with appropriate id. And set it instead of the original User object.
Did you using multiple Transaction managers from the same service class.
Like, if your project has two or more transaction configurations.
If true,
then at first separate them.
I got the issue when i tried fetching an existing DB entity, modified few fields and executed
session.save(entity)
instead of
session.merge(entity)
Since it is existing in the DB, when we should merge() instead of save()
you may be modified primary key of fetched entity and then trying to save with a same transaction to create new record from existing.

Java Jpa #GeneratedValue smart generation for both null and non-null values

I have 10-15 entities in my local postgres database.
All entities contains one identity of type Integer. See the code fragment down below.
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
#NonNull
private Integer id;
My CRUD (T is my entity) class contains create method for inserting new entities into appropriate table
private final JpaRepository<T, Integer> jpaRepository;
private final EntityManager entityManager;
private final Class<T> clazz;
#Override
public T create(T entity) {
return getJpaRepository().saveAndFlush(entity);
}
I am trying to achieve next goals:
If I call create method where entity contains some value (!= null) for id field then save it exactly with that id (not generated).
If I call create method where entity contains value == null - then create it with auto-generated id as database provide.
Why i need this?
I'm trying to fill in-memory small database with entities that I grab from JSON entity and save it with same id's.
My problem is that annotation #GeneratedValue(strategy = GenerationType.AUTO/SEQUENCE/TABLE/IDENTITY) not gives me such things.
If I remove GeneratedValue annotation then null values will not be handled properly.
I will highly appreciate your suggestions.
Add CommentCollapse 
Spring Data is no magic and if you look at the implementation of the saveAndFlush method, or rather at the implementation of save which it calls,
you'll see that it checks if (entityInformation.isNew(entity)).
How does it check isNew? Simply by checking whether the ID is NULL.
But you can simply use the entity manager directly! merge will do what you want. It's there to bring detached entities back into the managed state and copy its changes to the peristed entity. But if no persisted entity exists, it will simply create it.
Not that you do not want to always use merge instead of persist, this comes at a cost. See How do persist and merge work in JPA

How do I stop spring data JPA from doing a SELECT before a save()?

We are writing a new app against an existing database. I'm using Spring Data JPA, and simply doing a
MyRepository.save()
on my new entity, using
MyRepository extends CrudRepository<MyThing, String>
I've noticed in the logs that hibernate is doing a Select before the insert, and that they are taking a long time, even when using the indexes.
I've searched for this here, and the answers I've found usually are related to Hibernate specifically. I'm pretty new to JPA and it seems like JPA and Hibernate are pretty closely intertwined, at least when using it within the context of Spring Data. The linked answers suggest using Hibernate persist(), or somehow using a session, possibly from an entityManager? I haven't had to do anything with sessions or entityManagers, or any Hibernate API directly. So far I've gotten simple inserts done with save() and a couple #Query in my Repositories.
Here is the code of Spring SimpleJpaRepository you are using by using Spring Data repository:
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
It does the following:
By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity will be assumed as new, otherwise as not new.
Link to Spring Data documentation
And so if one of your entity has an ID field not null, Spring will make Hibernate do an update (and so a SELECT before).
You can override this behavior by the 2 ways listed in the same documentation. An easy way is to make your Entity implements Persistable (instead of Serializable), which will make you implement the method "isNew".
If you provide your own id value then Spring Data will assume that you need to check the DB for a duplicate key (hence the select+insert).
Better practice is to use an id generator, like this:
#Entity
public class MyThing {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
private UUID id;
}
If you really must insert your own id and want to prevent the select+insert then implement Persistable, e.g.
#Entity
public class MyThing implements Persistable<UUID> {
#Id
private UUID id;
#Override
public UUID getId() {
return id;
}
//prevent Spring Data doing a select-before-insert - this particular entity is never updated
#Override
public boolean isNew() {
return true;
}
}
I created a custom method in the #Repository:
public void persistAll(Iterable<MyThing> toPersist) {
toPersist.forEach(thing -> entityManager.persist(thing));
}
If you provide your own ID value then Spring Data will assume that you need to check the DB for a duplicate key (hence the select+insert).
One option is to use a separate autogenerated ID column as Primary key but this option seems redundant. Because if you already have a Business/Natural ID that is unique then it is easier to make this as the #ID column instead of having a separate ID column.
So how to solve the problem?
The solution is to use #javax.persistence.Version on a new versionNumber column in all the tables. If you have a parent and child table then use #Version column in all the entity classes.
Add a column in the Entity class like this:
#javax.persistence.Version
#Column(name = "data_version")
private Long dataVersion;
add column in SQL file:
"data_version" INTEGER DEFAULT 0
Then I see that Spring data does not do Select before doing Insert.

Object returned by jpa named query is managed and changes are persisted. How?

I have an Entity (say Employee) and a find method which uses TypedQuery to execute a named query and return the Employee rows. When the properties of this returned Employee instance is changed it is persisted.
I am trying to figure out the JPA concept behind this and how is this different from update. Is it good to update single row like this if only few column values of the existing rows in db needs change.
Looking for pointers to JPA concept that explains this.
Here is the code snip.
#Entity
#NamedQueries({
#NamedQuery(name = "Employee.findInActiveEmployee", query = "SELECT e FROM Employee e" +
"WHERE some_prop = :something")
})
public class Employee implements Serializable {
#Id
#NotNull
#Column(name = "id")
private String id;
#Column(name = "name")
private int name;
//so and so properties
//getter and setters
}
the finder method
TypedQuery<Employee> query = getEntityManager().
createNamedQuery("Employee.findInActiveEmployee", Employee.class);
query.setParameter("someproperty", "somevalue");
try {
return query.getSingleResult();
} catch (NoResultException e) {
throw new NoSuchObjectException("somevalue");
}
It's not really different from update.
In JPA you usually don't need to explicitly merge changes, since the JPA implementation will keep track of what data has changed in managed objects (i.e. entities the EntityManager knows about, such as ones that it has just loaded for you) and will make sure to save those changes to the underlying database.
If you don't want that, you can explicitly detach the entity with em.detach(Object o);, so the EntityManager no longer manages it . After that you'll need to perform merge() to update any changes.
Entities you get back from JPQL-Queries are managed by the EntityManager. In other words, they are atached and there is no need to merge them (like you would need to do for detached entities).
If you alter the entities you got back from the query and you have an open transaction the changes will be committed back to the database.
If you want to update a large number of entities a at the same time or your entities contain some members that have a really large serialized footprint then it might pay of performance-wise to use JPQL Updates.

Hibernate zeroToOne

I am trying to establish a relationship between 2 entities which would be zero-to-one. That is, the Parent can be saved without the associated Child entity and also along with the assoicated Child.
Following are the 2 Entity classes...
Employee (Parent)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="EMP_NAME")
private String name;
#PrimaryKeyJoinColumn
#OneToOne(cascade = {CascadeType.ALL})
private EmployeeInfo info;
#Column(name="EMP_ENUM")
private Integer enumId;
EmployeeInfo (Child)
public class EmployeeInfo {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="EMPLOYEE_EMAIL")
private String email;
With such kind of a relation and id column of the only Parent (Employee) table set to AUTO INCREMENT in MySql DB, the problem is that while saving a Parent->Child object graph, I get the following exception
org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [insert into EMP_INFO
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
I tried setting the Child Table's Id property to AUTO INCREMENT in the DB , and the persistence of such a Parent->Child object graph is successful.
However, the problem described here surfaces, because I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object, and hence do NOT want to have AUTO INCREMENT on the Child's id column.
One solution could be not use the PrimaryKeyJoinColumn, but use a particular JoinColumn, but that adds an unnecessary column to my existing Table.
Has anyone come across such a problem? If yes, any pointers would be much helpful.
Finally, I got it working thanks to Pascal and some googling from my side. Apparently, I cannot use the Native key generator for such relationships where the parent can exist without the child (optional = true).
The thing that worked finally was the following, leaving me the downside of having to deal with Hibernate specific annotation (#GenericGenerator) and also having to make-do with bi-directional relationships instead of the unidirectional that I wanted.
Employee (Parent) class remains unchanged as above. It has AUTO INCREMENT on the Id column.
As for the child class (EmployeeInfo) it changed to the following, and again WITHOUT having the AUTO INCREMENT set on the Id column.
#Table(name="EMP_INFO")
#Entity
public class EmployeeInfo {
#Id
#GeneratedValue(generator="foreign")
#GenericGenerator(name="foreign", strategy = "foreign", parameters={
#Parameter(name="property", value="verifInfo")})
private Long id;
#OneToOne(optional=false)
#JoinColumn (name="id")
private Employee emp;
#Column(name="EMPLOYEE_EMAIL")
private String email;
This helped me achieve what I wanted but on the downside, GenericGenerator is not a JPA annotation, it is a hibernate annotation, and sadly I have to make do with that as of now because JPA does not currently support this(or any similar) annotation.
Anyway, it helps to get through such cases :-)
I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object.
The optional attribute of a OneToOne is true by default, which is what you want.
However, you are somehow misusing the #PrimaryKeyJoinColumn here (well, it actually depends on what you really want to achieve but your current combination of annotations is not correct).
IF you want to map a OneToOne with a shared primary-key, use the #PrimaryKeyJoinColumn. But in that case, don't use a GeneratedValue on EmployeeInfo and set the id manually or, if you don't want to set it manually, use the Hibernate specific foreign generator that I already mentioned in your previous question. Check also the related question mentioned below.
And IF you do not want to use a shared primary key (like in your current code since you're trying to get the id generated by the database), then do not use the PrimaryKeyJoinColumn.
You have to make a choice.
References
JPA 1.0 specification:
9.1.32 PrimaryKeyJoinColumn Annotation
Related question
JPA Hibernate One-to-One relationship.

Categories