Až Vám zčerná obrazovka, přejděte na Linux!

Proč používat Linux
Zobrazují se příspěvky se štítkemHibernate. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemHibernate. Zobrazit všechny příspěvky

úterý 15. června 2010

Jak vyřešit ORA-01591: zámek uplatněný spornou distribuovanou transakcí

Tak jsem si jednou hrál s Hibernate a automatickým vytvářením tabulek nad skoro produkční databází Oracle 10 a dostal jsem se do jedné poněkud choulostivé situace. Při deployi earu jsem dostával
ORA-01591: zámek uplatněný spornou distribuovanou transakcí
Chvíli jsem si myslel, že to má na svědomí špatně navržený deployment Hibernate. Po čase jsem začal Gůglit ve vodách mimo Javu a dostal jsem se k jednoduchému řešeni:

Jedná se o problém, kdy transakce selhala v okamžiku, kdy se dostala do stavu prepared. Jediné řešení je ji odstranit. A řešení jsou dvě: buď se k ní připojit a rollbacknout, nebo ji smahnout přímo v databázi.

Druhé řešení je poměrně elegantní. Oracle definuje SQL příkaz ROLLBACK FORCE a jemu odpovídající PL/SQL funkci dbms_transaction.rollback_force(xid). V tabulce "SYS"."PENDING_TRANS$" je uložen záznam o všech čekajících transakcích. Ve sloupci LOCAL_TRAN_ID je XID.

Stačí se tedy přihlásit jako uživatel SYSTEM s funkcí SYSDBA (například v sqlplus), spustit
exec dbms_transaction.rollback_force(moje_xid)
a je vymalováno.

pondělí 31. května 2010

Hibernate error - identifier altered from 1 to 1

Nějakou dobu jsem laboroval s toto poněkud podivnou hláškou - jak může změna z 1 na 1 vygenerovat chybu? A kde se vůbec ta změna bere, když jedinou operaci, kterou dělám, je session.save()?

Nakonec jsem chybu našel. Při laborování s formátem dat v databázi a ručním copy&paste psaní hbm.xml jsem zavlekl typovou nekonzistenci do dat. V databázi (PostgreSQL) jsem měl u ID Integer (což onu chybu ale nevyvolá), v hbm.xml java.lang.Integer, ale ve třídě java.lang.Long. No a protože long nelze bez přetypování uložit do intu, ona alterace byla na světě.

Řešení tohoto problému je tedy prosté. Zkontrolujte si, že vám PŘESNĚ sedí jednotlivé typy v Javě a v hbm.xml

Kéž to někomu pomůže.

středa 9. dubna 2008

Hibernate a tabulky bez primárních klíčů

Jsou lidi, kteří tvrdí, že mít tabulku bez primárního klíče je nesmysl. A proto se bez něj Hibernate neumí pohnout. Já s nimi nesouhlasím.
Mějmež jednoduchý příklad: chceme uložit přichodivší fakturu do databáze. Faktura obsahuje hlavičku (kdo, kdy,...) a několik položek. Každá položka faktury má pevně danou strukturu. Z hlediska databázisty jednoduchý případ jak facka. Jedna tabulka pro fakturu (ID faktury, hlavička a stav), druhá pro položky (id faktury jako cizí klíč a atributy položky (cena, množství,...).
No jo, ale zkuste toto udělat v Hibernate! Jediná cesta, kterou jsem vygoogloval, je vytvořit fake_id pro každou položku každé faktury! FUJ! řeknou kování DBáci. A já s nimi.
Možná, že znáte jinou cestu. Rád se ji dozvím.

Hibernate a Primary Foreign Key

Tak jsem si dneska pořádně naběhl. Chtěl jsem si dvě tabulky provázat přes sdílený klíč, který byl v první Primary Key (tam záznam jako první vzniká) a po zpracování dat uložit do jiné tabulky řádek, jehož ID odpovídá tomu prvnímu, tedy bude hrít roli Primary a Foreign Key zároveň. Principiálně nic složitého.
Nechal jsem si v Idei vygenerovat příslušné anotace:

public class DataBindingEntity {
private long recordId;

@Id
@Column(name = "RECORD_ID", nullable = false)
public long getRecordId() {
return recordId;
}

public void setRecordId(long recordId) {
this.recordId = recordId;
}


private HibertestEntity hibertestByRecordId;

@OneToOne
@JoinColumn(name = "RECORD_ID", referencedColumnName = "ID", nullable = false)
public HibertestEntity getHibertestByRecordId() {
return hibertestByRecordId;
}

public void setHibertestByRecordId(HibertestEntity hibertestByRecordId) {
this.hibertestByRecordId = hibertestByRecordId;
}
...
}

public class HibertestEntity {
private long id;

@Id
@GeneratedValue(generator="triggerAssigned")
@GenericGenerator(name = "triggerAssigned",
strategy = "jpl.hibernate.util.TriggerAssignedIdentityGenerator")
@Column(name = "ID", nullable = false, length = 22)
public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}
private DataBindingEntity dataBindingById;

@OneToOne(mappedBy = "hibertestByRecordId")
public DataBindingEntity getDataBindingById() {
return dataBindingById;
}

public void setDataBindingById(DataBindingEntity dataBindingById) {
this.dataBindingById = dataBindingById;
}
...
}


Jenže jsem si parádně naběhl. Během inicializace sezení jsem chytil NPE:

13:04:59,125 ERROR [[acceptor]] Allocate exception for servlet acceptor
java.lang.NullPointerException
at org.hibernate.cfg.OneToOneSecondPass.doSecondPass(OneToOneSecondPass.java:135)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1130)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:296)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1286)


Zakomentování OneToOne propert na obou stranách problém vyřešilo. No, vyřešilo ... :-(

takže si to musím řešit ručně. Nedá se nic dělat.

pondělí 7. dubna 2008

IDEA 7, Hibernate a DB generovaná ID

Musím uznat, že ač je práce s Javím perzistorem Hibernate v IntelliJ IDEA celkem přímočará, dostanete se do stejných potíží, jaké u Hibernate mají i ostatní uživatelé. IDEA je přeci jenom vývojový nástroj, ikdyž geniální. Nic míň, ale také nic víc.
Potřeboval jsem qůli klastrovatelnosti nechat databázi generovat IDčka záznamů. V Oracle je to jednoduché, jde to naklikat v Oracle XE konzoli. ve výsledku asi nejkratší možný SQL záznam vypadá:

CREATE TABLE "HIBERTEST"
( "ID" NUMBER,
"DATA" BLOB,
CONSTRAINT "HIBERTEST_PK" PRIMARY KEY ("ID") ENABLE
)

CREATE OR REPLACE TRIGGER "BI_HIBERTEST"
before insert on "HIBERTEST"
for each row
begin
select "HIBERTEST_SEQ".nextval into :NEW.ID from dual;
end;


Pro pouhé uložení záznamu vám stačí nastavit IDčku hodnotu -1 (pro zachování nullable=false) a Oracle to přeplácne za Vás. Potíž nastane v okamžiku, kdy chcete s onou zaperzistentněnou beanou dál pracovat a mít v ní ono ID. Naštěstí bratr Google opět pomohl.
Jean-Pol Landrain napsal velice triviální a nádherný PostInsertGenerator, který to řeší velice elegantně. Podívejte se sami.
Pak stačí entitě v IDEA přidat anotaci

@GeneratedValue(generator="triggerAssigned")
@GenericGenerator(name="triggerAssigned",
strategy = "jpl.hibernate.util.TriggerAssignedIdentityGenerator")

a je vymalováno.

IDEA 7 aneb Tak dlouho jsem se bránil hibernate, až se ucho utrhlo

Nastoupil jsem do nového zaměstnání - do hradišťské firmy BARCO a začal psát J2EE aplikaci postavenou nad SQL. O hibernate jsem nechtěl ani slysšet, protože když jsem naposledy viděl jeho konfiguraci, jímala mne hrůza a běs.
Ale nainstaloval jsem si novou IntelliJ IDEA, IMHO nejlepší nástroj na vývoj aplikací v Javě, která podporu Hibernate v sobě obsahuje. Tak jsem to zkusil. V Oracle XE konzoli jsem si nadefinoval tabulku, dal do ní cvičně jeden řádek a zkoušel jsem, co s tím IDEA dokáže za kouzla. A byl jsem mile přeqapen.
Postup je velice jednoduchý. Ze všeho nejdřív si v projektu nadefinujte DataSource ukazující na onu tabulku (Menu->Tools->Data Sources..., nezapomeňte na Refresh Tables jako já :-) ). Pak vytvoříte v projektu nový modul typu Hibernate, zasociujete jej s Data Sources, přidáte Deskriptor (IDEA sama nabídne jediný možný ;-) ) a potvrdíte. Pro modul víc nepotřebujete.
Poté se už jenom v projektovém navigátoru přepnete do pohledu Java EE: Structure, kliknete na nový Hibernate projekt a v kontextovém menu vyberete Generate Persistence Mapping->By Database Schema a pak jen zíráte.