We've having a problem doing an insert into two tables in a single transaction in our H2 database using EclipseLink JPA.
The database schema looks something like ...
Table A {...,
IP_ADDRESS Varchar2( 15 ) not null
}
Table B {...,
IP_ADDRESS Varchar2( 15 ) not null,
constraint MY_FK foreign key (IP_ADDRESS) references A
}
The problem we are having is that the foreign key constraint on B is making the transaction fail.
We're doing this:
entityManager.getTransaction().begin();
entityManager.merge ( Save to A transaction );
entityManager.merge ( Save to B transaction );
entityManager.getTransaction().commit();
It looks like JPA is trying to do the save to B first and failing.
Or perhaps it is checking the constraint on B first?
At any rate, what we'd like to do ideally is something like what I recall from many years ago, using PL/SQL on oracle:
BEGIN
Do the insert on A;
Do the insert on B;
commit;
End;
I don't think Oracle looked at the constraint until the transaction was complete. Or allowed it complete because the uncommitted version of the transaction did not violate the constraint.
Any thoughts? I looked at the documentation and have not found anything helpful yet.
Thanks
I believe I have found the problem, although I would love a confirmation from somehow who knows JPA better than I do.
The issue here was that there was no definition of the foreign key constraint in the entity classes representing the two tables.
As a result, the EntityManager knew nothing about the relationship between the two tables and picked an arbitrary insert order.
The transaction then failed because JPA tried to insert the table B row before the the row for table A.
I fixed this by adding a #ManyToOne relationship in table B, and a #OneToMany relationship table A. I set the fetch type to lazy as the relationship definition as there is no need to use any fetch results.
In the Table A entity class:
// bi-directional one-to-many association to PositionHostPermission
#OneToMany( mappedBy = "the a entity", fetch=FetchType.LAZY )
private Set<B> bInstances;
In the Table B entity class:
//bi-directional many-to-one association to ipAddress
#ManyToOne( fetch= FetchType.LAZY )
#JoinColumn(name="IP_ADDRESS",insertable=false, updatable=false )
private A aTableInstance;
Related
I have a query in which I'm joining a number of tables to my original Person entity. A Person may have multiple Child relations (OneToMany), and a Child may have a School they go to (ManyToOne). Problem is, I don't need the entire School entity that connects to each child, only their id, which is already stored on Child.
I'm using a Paginator to iterate through the results and I use HYDRATE_ARRAY to reduce overhead of the ORM parsing data to entity objects. But the id fields of unfetched relations are not returned this way, and thus, the School id isn't either.
I may join the School entity too, but since the identity is already stored on the Child records, I don't see why I should further reduce performance by having the database join another table. Fetching results as entity objects would also solve the problem, but also at the cost of performance. How can I get the id to apper the results without having to unnecessarily join the the School entity or having to hydrate the results as objects?
$query = $em->getRepository(Entity\Person::class)->createQueryBuilder('p');
$query
->select([
'p as person',
'w.name as workplace_name',
'c',
])
->leftJoin('p.children', 'c') //Entity\Child
->leftJoin('p.workplace', 'w') //Entity\Company
//...
;
$paginator = new Paginator($query);
$paginator->getQuery()
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
You can use Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS to include the foreign key column values in the result:
$paginator->getQuery()
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
which:
The includeMetaColumns query hint causes meta columns like foreign keys and discriminator columns to be selected and returned as part of the query result.
Reference
Doctrine\ORM\Query documentation
How to get association foreign key IDs in Doctrine 2 without loading the associated object?
Getting only ID from entity relations without fetching whole object in Doctrine
I'm new to sqlite and sql in gerneral so I don't know if my approach is reasonable.
I want to model inventory items that can be created, lent, returned and discarded.
I want to model this using two tables, one for items, containing an id, a name and a reference to the last transaction (created, lent, returned, ...) and a table of transactions containing an id transaction type, date, and a reference to the item.
Since creating only one table leaves the database in an inconsitent state with one table referencing a non existant table I thought of using a transaction to crate both tables at once, and defining the foreign keys as deferrable. Creation of a new item would have to be done together in one transaction with creating a "created" event to leave the database in a consistent state.
The following code gives me the error Query Error: not an error Unable to execute multiple statements at a time in sqliteman on linux.
PRAGMA foreign_keys = ON;
begin transaction;
create table items (
id integer primary key,
name char(30),
foreign key (last_transaction) references transactions(transaction_id) DEFERRABLE INITIALLY DEFERRED
);
create table transactions(
transaction_id integer primary key,
text char(100)
foreign key (item) references items(id) DEFERRABLE INITIALLY DEFERRED
);
commit transaction;
Does my approach make sense at all?
If yes, why does the code not work? (Did I make a mistake somewhere, or is what I'm trying impossible in mysql?)
Note: simply creating the tables in one transaction without the foreign key constraints gives the same error. (Could this be a similar Problem to: this question)
I have 3 tables: User, Department and UserDepartment. User can be associated with multiple departments and department can be associated with multiple users. I use many-to-many fluent mapping on both sides like this:
For Department
HasManyToMany(x => x.Users)
.Table("UserDepartment")
.ParentKeyColumn("DepartmentId")
.ChildKeyColumn("UserId")
.AsSet()
.Cascade.All();
For User
HasManyToMany(x => x.Departments)
.Table("UserDepartment")
.ParentKeyColumn("UserId")
.ChildKeyColumn("DepartmentId")
.AsBag()
.Inverse()
.Cascade.None();
All tables use HiLo Id generator and almost similar mapping, like this:
Id(p => p.Id).GeneratedBy.HiLo("HiLo", "NextHi", "32", "ForTable = 'UserDepartment'");
When I try to add some users to department nhiberante fails with an error:
could not execute batch command.[SQL: SQL not available]. And inner
exception: Cannot insert the value NULL into column 'Id', table
'test.dbo.UserDepartment'; column does not allow nulls. INSERT
fails.\r\nThe statement has been terminated.
I have the same HiLo generator working with other tables, so I'm pretty sure it doesn't cause this failure.
Can someone shed some light on this problem? I can recall having problem with null Id insert in the past and it was solved by using inverse mapping on collection, but I cannot use inverse on both sides, so I need another solution.
Finally I have found the solution. There are two ways:
Remove synthetic ID from table and use composite ID instead.
Use XML mapping to specify idbag behaviour.
as expained in: NHibernate, HiLo and many-to-many association
I'm working on a simple project using sqlite, JPA and eclipseLink.
First I create my Person table in my database with this:
CREATE TABLE Person (
idPerson INTEGER PRIMARY KEY AUTOINCREMENT,
firstname TEXT DEFAULT 'NULL',
birthdate DATETIME DEFAULT 'NULL'
)
and then add a new test entry (in order to generate the sqlite_sequence table)
INSERT INTO [Person] ([firstname], [birthdate]) VALUES ('Foo', '1145-11-12 00:00:00')
All the successive insertion are done using JPA, where in the Person class I use this notation for the person id:
#GeneratedValue(generator="sqlite_person")
#TableGenerator(name="sqlite_person", table="sqlite_sequence",
pkColumnName="name", valueColumnName="seq",
pkColumnValue="Person")
#Column(name="idPerson")
private int id;
The first JPA insertion is ok (that is, new new inserted person has id = 2) but then I get an increment of 50 instead of only 1 (so the third inserted person has id = 52, the fourth 102 and so on).
I read that "making modifications to this table will likely perturb the AUTOINCREMENT key generation algorithm" [ref]
Is my problem related to this, even if in theory I'm not modifying that table?
Any kind of suggestion in order to resolve the problem?
You are using Autoincrement in the database, which means the database will assign a value to the id when it is inserted, but then you tell the JPA provider to use Table generation. Table generation requires that the JPA provider use a special table to keep track of sequence values, looking it up and assigning it before inserting the entity row in the database. This conflicts with what you set up in the database.
http://wiki.eclipse.org/EclipseLink/Examples/JPA/PrimaryKey explains sequencing fairly well. You will need to try using #GeneratedValue(strategy=GenerationType.IDENTITY) instead of table generation so that inserts by JPA use the same sequence allocation as inserts outside of JPA.
At the end, in order to solve the problem, I just added two optional elements for the TableGenerator annotation (i.e. initialValue, allocationSize).
So the new annotation for the ID is like this:
#GeneratedValue(generator="sqlite_person")
#TableGenerator(name="sqlite_person", table="sqlite_sequence",
pkColumnName="name", valueColumnName="seq",
pkColumnValue="Person",
initialValue=1, allocationSize=1)
#Column(name="idPerson")
private int id;
I think it works also without the initial value, but like this I also avoid to insert random entries (because it seems that the sqlite_sequence table is automatically generated already when I create the Person table)
I have an Entity Framework 4 design that allows referenced tables to be deleted (no cascade delete) without modifying the entities pointing to them. So for example entity A has a foreign key reference to entity B in the ID field. B can be deleted (and there are no FK constraints in the database to stop that), so if I look at A.B.ID it is always a valid field (since all this does is return the ID field in A) even if there is no record B with that ID due to a previous deletion. This is by design, I don't want cascading deletes, I need the A records to stick around for a while for auditing purposes.
The problem is that filtering out the non-existing deleted records is not as easy as it sounds. So for example if I do this:
from c in A
select A.B.somefield;
This results in a OUTER JOIN in the generated SQL so it's picking up all the A records even if they refer to missing B records. So, the hack I've been using to solve this (since I can't figure out a better way!) is do add a where clause to check a string field in the referenced B records. If that field in the B entity is null, then I assume B doesn't exist.
from c in A
where c.B.somestringfield != null
select A.B.somefield;
seems to work IF B.somestringfield is a string. If it is an integer, this doesn't work!
This is all such a hack to me. I've thought of a few solutions but they are just not practical:
Query all tables that reference B when a B is deleted and null out their foreign keys. This is so ugly, I don't want to have to remember to do this if I add another entity that references B in the future. Not to mention a huge performace delay resolving all the references whenever I delete something.
Add a string field to every table that I can count on being there that I can check to see if the entity exists. Blech, I don't want to add a database field just for this.
Implement a soft delete and keep all the referencial integrity intact - essentially set up cascading deletes, but this is going to result is huge database bloat since I can't clean up a massive amount of records due to the references. No go.
I thought I had this problem licked with the "check if a field in the referenced entity is null" trick but it breaks under conditions that I don't completely understand (what if I don't have any strings in the referenced table? What kinds of fields will work? Integers won't.)
As an example if I have an integer field "count" in entity B and I check to see if it's null like:
from c in A
where c.B.count != null
select c.B.count;
I get a bunch of records with null for count mixed in with the results, and in fact the query bombs out with an "InvalidOperationException: The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
So I need to do
from c in A
where c.B.count != null
select new { count = (int?)c.B.count };
to even see the null records. So this is pretty baffling to me how that query can result in null records in the results at all.
I just discovered something, if I do an explicit join like this, the SQL is INNER JOIN and everything works great:
from c in A
join j in B on A.B.ID equals j.ID
select c;
But this sucks. I'll have to modify a ton of queries to add explicit join clauses instead of enjoying the convenience of the relationship fields I get with the EF. Kinda defeats the purpose and adds a buch more code to maintain.
When you say that your first code snippet creates an OUTER JOIN then it's the case because B is an optional navigation property of entity A. For a required navigation property EF would create an INNER JOIN (explained in more detail here: https://stackoverflow.com/a/7640489/270591).
So, the only alternative I see to your last code snippet (using explicit join in LINQ) - aside from using direct SQL - is to make your navigation property required.
This is still a very ugly hack in my opinion which might have unexpected behaviour in other situations. If a navigation property is required or optional EF adds a "semantic meaning" to this relationship which is: If there is a foreign key != NULL there must be a related entity and EF expects that you don't have removed the enforcement of the FK constraint in the database.