used: hibernate 3.6.10, maven 2, postgres 9.
I have code that must work but it doesn't.
before I used hibernate 3.6.2 and got really frustrated error:
java.lang.ClassCastException: org.hibernate.action.DelayedPostInsertIdentifier cannot be cast to java.lang.Long
But after updating to 3.6.10 error became more sensible:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist
Code is a standart domain model:
Entity:
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Entity
#Table(schema = "simulators", name = "mySimulator_card")
public class MySimulatorCard {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "account_number", unique = true, nullable = false)
private String accountNumber;
etc...
DAO:
public abstract class AbstractDao<E, PK extends Serializable> implements Dao<E, PK> {
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
#PersistenceContext(unitName = "MySimulator")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public abstract Class<E> getEntityClass();
#Override
public void persist(E e) {
getEntityManager().persist(e);//<-- some thing wroooong
}
etc...
And according table:
CREATE TABLE simulators.mySimulator_card
(
id bigserial NOT NULL,
account_number character varying(255) NOT NULL,
etc...
CONSTRAINT mySimulator_card_pk PRIMARY KEY (id),
CONSTRAINT mySimulator_card_account_fk FOREIGN KEY (account_id)
REFERENCES simulators.mySimulator_account (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT mySimulator_card_currency_fk FOREIGN KEY (currency_id)
REFERENCES simulators.mySimulator_currency ("name") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT mySimulator_card_product_fk FOREIGN KEY (product_id)
REFERENCES simulators.mySimulator_product (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT mySimulator_account_account_number_uq UNIQUE (account_number),
CONSTRAINT mySimulator_card_san_uq UNIQUE (san)
)
WITH (
OIDS=FALSE
);
ALTER TABLE simulators.mySimulator_card OWNER TO functional;
I suppose something wrong with context in functional tests because production is work fine. Also when I tried to use this trick to get session from entityManager:
EntityManager em = getEntityManager();
HibernateEntityManager hem = em.unwrap(HibernateEntityManager.class);
Session session = hem.getSession();
session.persist(e);
I've got the error:
java.lang.IllegalStateException: No transactional EntityManager available
Now sure that it is a good idea to post all test config because there are a lot of workaround stuff.
Any idea?
This error is raised among others when you try to persist a value in a column that is autogenerated.
Check if you're passing a value to persist for the column id, which you've marked as:
#GeneratedValue(strategy = GenerationType.IDENTITY)
Regards.
Related
I want to get an object of EventDate using primary key. Following is the query i'm executing
EventData eventData = entityManager.find(EventData.class, eventdataid);
After executing this command in console i'm getting the query as
select eventsajgj0_.FILE_ID as FILE_ID8_14_0_, eventsajgj0_.id as
id1_12_0_, eventsajgj0_.id as id1_12_1_, eventsajgj0_.CODE as CODE2_12_1_,
eventsajgj0_.DATE as DATE3_12_1_, eventsajgj0_.FILE_ID as FILE_ID8_12_1_,
eventsajgj0_.MILLIS as MILLIS4_12_1_, eventsajgj0_.ORDER_NR as
ORDER_NR5_12_1_, eventsajgj0_.TYPE as TYPE6_12_1_, eventsajgj0_.VALUE as
VALUE7_12_1_ from eventdata eventsajgj0_ **where eventsajgj0_.FILE_ID=?**
order by eventsajgj0_.ORDER_NR
Please note the where clause in above query is against file_id(foreign key) and not id(eventdata primary key)
The dao structure is as follows
public class EventData implements Serializable {
private static final long serialVersionUID = 1L;
public EventData() {
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="FILE_ID")
private ApplicationFile file;
getter & setters
}
public class ApplicationFile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
bi-directional many-to-one association to Event
#OneToMany(mappedBy="file", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#OrderBy("orderNr")
private List<EventData> eventsajgjd;
getter & setters
}
my question is, why is it querying using file_id and not id when i'm executing a query on eventdata table.
PS:if i change fetch type of ApplicationFile as LAZY then the query executed is on id and not on file_id.
(added from Comment:)
CREATE TABLE eventdata (
ID int(11) NOT NULL AUTO_INCREMENT,
FILE_ID int(11) DEFAULT NULL,
PRIMARY KEY (ID),
KEY eventdata_ibfk_1 (FILE_ID),
CONSTRAINT eventdata_ibfk_1 FOREIGN KEY (FILE_ID)
REFERENCES files (ID) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=297502 DEFAULT CHARSET=utf8
I bet because you mapped the EventData / ApplicationFile bidirectionally (you have an attribute of type List<EventData> in ApplicationFile entity
So loading an EventData means eagerly loading the related ApplicationFile and so eagerly loading all related EventData.
I suppose that the related ApplicationFile instance is already in EntityManager L1 cache (otherwise the query should join on files table)
I'm learning Hibernate (Spring) and facing strange issue with removing child entities from the parent one.
Here is what I have:
Parent entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "company_id", referencedColumnName = "id")
List<CompanyObject> companyObjects;
}
Child entity:
#Entity
public class CompanyObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Enumerated(EnumType.STRING)
ObjectType type;
#ManyToOne
#JoinColumn(name = "company_id")
Company company;
}
Here is my table definitions:
CREATE TABLE `company` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8
CREATE TABLE `company_object` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`type` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK__company` (`company_id`),
CONSTRAINT `FK__company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
And, also, I have the following update method:
// some code here
public void update(CompanyDto dto) {
Company company = repository.getCompanyById(companyId);
repository.save(dto.merge(company));
}
// some code here
public class CompanyDto {
private List<CompanyObjectDto> companyObjects = new ArrayList<>();
public Company merge(Company company) {
company.getCompanyObjects().clear();
for (CompanyObjectDto dto : companyObjects) {
company.getCompanyObjects().add(dto.to(company));
}
return company;
}
}
public class CompanyObjectDto {
ObjectType type;
public CompanyObject to(Company company) {
CompanyObject object = new CompanyObject();
object.setType(this.getType());
object.setCompany(company);
return object;
}
}
And as soon as I launch update method, I get the following error: java.sql.SQLWarning: Column 'company_id' cannot be null. I investigated this a little bit and found out that if I comment out company.getCompanyObjects().clear(); string it works ok, so it seems there is some problem with cascading delete action to company objects.
Could, please, somebody point me to my mistakes? Thanks.
You have mapped your entities Company and CompanyObject bidirectionally, i.e. both entities have a relation to the other entity.
In this case, there should only be one #Joincolumn and one entity must be selected as the owning entity, with the other entity marking the relation with a 'mappedby' (see http://docs.oracle.com/javaee/6/api/javax/persistence/ManyToOne.html).
You are getting error because you are removing object's from List and then use the same List as a reference to your Company object. See below code :
private List<CompanyObjectDto> companyObjects = new ArrayList<>(); //Stmt 1
Above code is used to define list which will be reference in your below code :
company.getCompanyObjects().clear(); //It will clear out all objects
for (CompanyObjectDto dto : companyObjects) { //Iterating over empty list defined in stmt 1.
company.getCompanyObjects().add(dto.to(company));
}
So your foreign key will always be null which is not permitted and throws exception.
And your code works when you comment out List#clear line because in that scenario, list already have some referenced objects which didn't modify.
I'm attempting to use Spring JPA/Hibernate to maintain a Parent table with a child table related based on an Id. If I attempt to insert the same object twice, the Parent table gets "updated" properly, but the child table is always inserted to even though the entry in the child table is already present.
CREATE TABLE `parent_table` (
`parent_id` int(11) NOT NULL AUTO_INCREMENT,
`ex_column_1` int(11) NOT NULL, // there is a unique constraint on this field - I just didn't include it
`ex_column_2` int(11) NOT NULL,
`child_id` int(11) NOT NULL,
PRIMARY KEY (`parent_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Child Table:
CREATE TABLE `child_table` (
`child_id` int(11) NOT NULL AUTO_INCREMENT,
`child_col_1` varchar(200) DEFAULT NULL,
PRIMARY KEY (`child_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
POJOs:
#Entity
#Table(name = "parent_table")
public final class Parent {
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "parent_id")
private long id;
#Id
#Column(name = "ex_column_1")
private int exampleField;
#Column(name = "ex_column_2")
private int exampleField2;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "child_id", unique = true)
private Child c;
public Parent(/*params*/) {}
// getters ...
#Entity
#Table(name = "child_table")
public static final class Child {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "child_id")
private long id;
#Column(name = "child_col_1")
private exampleChildField;
public Child(/*params*/) {}
// getters ...
}
}
Actual example of how POJO's are constructed and saved:
#RestController
#RequestMapping("/parents")
public final class ParentController {
private final ParentRepository parentRepository;
#Inject
public ParentController(final ParentRepository parentRepository) {
parentRepository = parentRepository;
}
#RequestMapping(value = "", method=RequestMethod.PUT)
public void updateParents(#RequestBody Parent[] parents) {
// ignore input for now, test by constructing object
Parent.Child c = new Parent.Child(0L, "test 1");
Parent p = new Parent(0L, "unique_column_1", "column_2", c);
Set<Parent> pSet = new HashSet<>();
pSet.add(p);
parentRepository.save(pSet);
}
}
Repository Layer:
public interface ParentRepository extends CrudRepository<Parent, Long>, ParentRepositoryCustom {}
public interface ParentRepositoryCustom {
void save(Set<Parent> parents);
}
#Repository
final class ParentRepositoryImpl implements ParentRepositoryCustom {
private final EntityManager entityManager;
#Inject
public EmployerRepositoryImpl(final EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
#Transactional
public void save(Set<Parent> parents) {
parents.forEach(parent -> {
Session session = (Session) entityManager.getDelegate();
session.saveOrUpdate(parent);
});
}
}
If the entry in either table doesn't exist, it persists both entities just fine and links the proper Ids between tables. The issue occurs if the same parent and child object are inserted again. The UPDATE occurs on the Parent table, but an INSERT on the child:
Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: insert into parent_table (ex_column_1, ex_column_2, child_id) values (?, ?, ?)
On second insert:
Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: update parent_table set ex_column_2=?, child_id=? where ex_column_1=?
How do I get EntityManager to persist these correctly if the entry already exists?
I think I've found issue. The client that is used to post new "parent" objects to the rest interface, by default, sets the primary key ids to 0. This works for the parent because ex_column_1 is an ID that's known at the time objects get created by some other mechanism.
On the other hand, the Child object only has a primary key as its Id and by setting it to zero, Spring attempts to find a related child row based on an id of zero, and always does an insert instead of an update if a child row already exists. Doing a simple check using the EntityManager.find() method on both objects then saving or merging fixed it.
When I try persist the entity below in the database, in a application which uses spring and hibernate, in the way it's presented, I get the error object references an unsaved transient instance - save the transient instance before flushing; but if I add the value cascade = CascadeType.ALL to the annotation #OneToOne (or add the annotation #Cascade), for each attribute I don't fill it's inserted an empty record in the database.
#Entity
#Table(name="cliente")
public class Cliente {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="usuario")
#Order(value=1)
private Usuario usuario;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pessoa_fisica")
#Order(value=2)
private PessoaFisica pessoaFisica;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pessoa_juridica")
#Order(value=3)
private PessoaJuridica pessoaJuridica;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="endereco_entrega")
#Order(value=4)
private Endereco endereco_entrega;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="endereco_cobranca")
#Order(value=5)
private Endereco endereco_cobranca;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="pedidos_do_cliente", joinColumns={#JoinColumn(name="fk_cliente")}, inverseJoinColumns={#JoinColumn(name="fk_pedido")})
#Order(value=6)
private List<Pedido> pedido;
}
Anyone can tell me how to fix this problem?
ps.: below the related method which handle this process:
controller
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
public String cadastra(#ModelAttribute("object") E object, BindingResult result, #RequestParam(value="file", required=false) MultipartFile file, #RequestParam(value="icone", required=false) MultipartFile icone) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
serv.cadastra(object);
serv.upload_picture(object, file, "picture");
serv.upload_picture(object, icone, "icone");
return "";
}
service
#Transactional
public void cadastra(E e) {
dao.persist(e);
}
dao class
#Transactional
public void persist(E transientInstance) {
sessionFactory.getCurrentSession().persist(transientInstance);
}
UPDATE
CREATE TABLE cliente
(
id serial NOT NULL,
endereco_cobranca integer,
endereco_entrega integer,
pessoa_fisica integer,
pessoa_juridica integer,
usuario integer,
CONSTRAINT cliente_pkey PRIMARY KEY (id),
CONSTRAINT fk_4taj5h8hxci6slrw0n3d336i7 FOREIGN KEY (endereco_entrega)
REFERENCES endereco (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_7a5l0l5enj00apelvmcdatkmm FOREIGN KEY (pessoa_juridica)
REFERENCES pessoa_juridica (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_iiibo76b56caciax4yl0jo0m6 FOREIGN KEY (pessoa_fisica)
REFERENCES pessoa_fisica (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_lwavy2v0wb0vmxisg7hbe3mbu FOREIGN KEY (usuario)
REFERENCES usuario (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_r2pyppeltv7xoe5quenf2l1gd FOREIGN KEY (endereco_cobranca)
REFERENCES endereco (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE cliente
OWNER TO klebermo;
In your view layer, are you creating new objects for the these linked columns? or in the controller that serves the form, are you adding 'new' objects to the model? It seems as though you have created empty java objects for these one-to-one columns which is why you cant save the parent without saving the children and why with cascade option it creates empty records.
My entities are:
the ID of device which is deiveID
has many-to-many relationship with
the ID of Lib which is rID
my test code is :
two new device entities want to set the same new libentity
the problem is :
if i use the same entitymanager to persist that 2 new device entities, it will be ok.
but if i use 2 different entitymanager instance to persist them ,the error"primary key violation" will come out. I think the entitymanger try to persist the libentity at the second time, which has already been persisted in the first time.
--------------deviceinfo entity ------------------------------------------------
#Entity
#Table(name="deviceInfo")
public class DeviceInfoEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long deviceId;
....
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(name = "device_lib", joinColumns = #JoinColumn(name = "deviceInfo_id",
referencedColumnName="deviceId"),
inverseJoinColumns = #JoinColumn(name = "lib_id", referencedColumnName="rId"))
private List<LibEntity> resourceList = null;
......
}
-------------------------lib entity ---------------------------------------------
#Entity
#Table(name="lib")
public class LibEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long rId;
#ManyToMany(mappedBy = "resourceList", cascade=CascadeType.ALL,
fetch=FetchType.LAZY, targetEntity=DeviceInfoEntity.class)
private List<DeviceInfoEntity> deviceInfolist = null;
.....
}
my test code is:
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("testPU");
EntityManager em = emFactory.createEntityManager();
LibEntity libEntity = new LibEntity();
DeviceInfoEntity dEntity = new DeviceInfoEntity();
dEntity.setName("dadadada");
dEntity.setLibEntity(libEntity);
DeviceInfoEntity dEntity2 = new DeviceInfoEntity();
dEntity2.setName("dadadadadddddd");
dEntity2.setLibEntity(libEntity);
em.getTransaction().begin();
em.persist(dEntity);
em.getTransaction().commit();
em.close();
EntityManager em2 = emFactory.createEntityManager();
em2.getTransaction().begin();
em2.persist(dEntity2);
em2.getTransaction().commit();
it will have the error:
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.LIB(RID)"; SQL statement:
INSERT INTO lib (RID) VALUES (?) [23505-165]
but if i use the same EntityManager the error will not happen. Is there anyone know whats the reason? is that caused by cascade=CascadeType.ALL?
You are corrupting your persistence context by assign detached objects to managed objects. Managed object should only reference other managed objects.
For dEntity2 you should set the libEntity to the result of a find(), getReference() or merge() of libEntity.
i.e.
dEntity2.setLibEntity(em2.find(libEntity.getClass(), libEntity.getId());
You could probably also call merge() instead of persist() and it should resolve the detached objects.