I'm working with a legacy database and have no DDL privileges.
I created a #MappedSuperclass that have #Embedded attributes:
#MappedSuperclass
public abstract class MyEntity {
#Embedded
private CreateInfo createInfo;
#Embedded
private UpdateInfo updateInfo;
}
Unfortunately, one table has a different column name for the above properties.
Is it possible to override the attributes on the actual Entity, if yes how?
Turns out that #AttributeOverride can be nested.
#Entity
#AttributeOverrides({
#AttributeOverride(name = "updateInfo.lastModifiedBy", column = #Column(name = "DIFF_NAME1"))
, #AttributeOverride(name = "updateInfo.lastModifiedDate", column = #Column(name = "DIFF_NAME2"))
})
public class Child extends MyEntity {...}
Related
I have a very simple set of tables modelling a many to many relationship.
Foo -< FooBar >- Bar
Which is working fine when I select data but when I try to create a new instance of the relationship I can getting errors because JPA is trying to insert nulls.
I am having to set both the #ManyToOne values as well as the key (which represent the same things) and this makes me think this is not setup correctly.
Question is therefore how do I correctly setup the annotations to create a new FooBar relationship?
Entities
#Entity
#Table(name = "FOO")
public class Foo implements Serializable {
#Id
#Column(name = "FOO_ID")
private int id;
#column(name = "FOO_DESC")
private String desc;
#OneToMany(mappedBy = "foo")
private List<FooBar> fooBars;
//getters / setters / hashCode / equals
}
#Entity
#Table(name = "BAR")
public class Bar implements Serializable {
#Id
#Column(name = "BAR_ID")
private int id;
#column(name = "BAR_DESC")
private String desc;
#OneToMany(mappedBy = "bar")
private List<FooBar> fooBars;
//getters / setters / hashCode / equals
}
Intersection Table (and Key)
#Embeddable
public class FooBarKey implements Serializable {
private int fooId;
private int BarId;
//getters / setters / hashCode / equals
}
#Entity
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#EmbeddedId
private FooBarKey key;
#MapsId("fooId")
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#MapsId("barId")
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
Creating Relationship
Finally I am creating a new intersection instance:
//foo and bar exist and are populated
FooBar fb = new FooBar();
FooBarKey fbk = new FooBarKey();
fbk.setFooId(foo.getId());
fbk.setBarId(bar.getId());
fb.setKey(fbk);
fb.setDesc("Some Random Text");
entityManager.persist(fb);
at which point JPA errors with the insert saying it cannot insert null into FOO_ID.
I have checked and at the point I persist the object, the key is populated with both Foo and Bar IDs.
If I add
fb.setFoo(foo);
fb.setBar(bar);
prior to the persist it works but should the #MapsId not effectively tell JPA to map using the key?
I presume that I should be setting both the #ManyToOne and key values which are logically the same thing so I must have something not configured correctly?
I am using Eclipselink if that makes a difference.
When you use mapsId, you are telling JPA that this relationship, and the value of the target entity's primary key, is used to set the mapping named within the mapsId value. JPA then uses this relationship mapping to set the foreign key AND the basic mapping value when it flushes or commits. In your case, you left the relationship NULL, which forces the FK to be null when it gets inserted.
This allows sequencing to be delayed, as you may not have the primary key generated in the referenced entity when creating and traversing the graph - JPA will calculate and populate the values and propagate them through the graph when it needs them.
If you aren't using the ID class in your model, the simplest solution is just to remove it and avoid the overhead:
#Entity
#IdClass(package.FooBarKey.class)
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#Id
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
public class FooBarKey implements Serializable {
private int foo;
private int bar;
}
IdClass has similar restrictions to what is needed for #EmbeddedId but with one more - the names within it must match the property names designated with #Id, but the types must be the same as the ID class within the referenced entity. Pretty easy if you are using basic mappings within Foo and Bar, but can be more complex.
Adding more to your composite key is easy:
#Entity
#IdClass(package.FooBarKey.class)
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#Id
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Id
private int someValue
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
public class FooBarKey implements Serializable {
private int foo;
private int bar;
private int someValue
}
JPA will populate your foreign keys for you when the relationships are not-null, but any other fields require either sequencing or your own mechanisms to ensure they are populated prior to insert, and all Ids should be treated as immutable within JPA.
I have defined several JPA entities in a Spring Boot application.
#Entity
public class EntityA {
#Id
#Column(name = "id")
private String id
// ...
//....other fields
}
#Entity
public class EntityB {
#EmbeddedId
private MyEmbeddedId id;
// ....other fields
}
public class MyEmbeddedId {
#Column(name = "id_a")
private String idA;
#Column(name = "color")
private String color;
}
EntityA is self-sufifcient, EntityB has a composite key, one of whose constituent is primary key of EntityA(represented by column id_a). So I use embedded key feature here. There is a one-to-many relationship between EntityA and EntityB. An instance of EntityA can be mapped to multiple instances of EntityB.
Now, my objective is to fetch EntityA with all its associated EntityB.
How to go about mapping for this?
Try below Entity mappings,
#Entity
public class EntityA {
#Id
#Column(name = "id")
private String id
#OneToMany(mappedBy = "myEmbeddedId.idA",
cascade = CascadeType.ALL)
private Set<EntityB> entityB;
// ...
//....other fields
}
#Entity
public class EntityB {
#EmbeddedId
private MyEmbeddedId myEmbeddedId;
// ....other fields
}
public class MyEmbeddedId {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="id_a")
private EntityA idA;
#Column(name = "color")
private String color;
}
When you load EntityA, it will load all associated EntityB.
I am using springboot and springdata with Mysql.
I have 2 entities, Customer & Order:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name="customer_id")
private long customerId;
}
I also have a repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Long> {
#Query("select o from Order o, Customer c where o.customerId = c.id")
Page<Order> searchOrders(final Pageable pageable);
}
The method has some more arguments for searching, but the problem is when I send a PageRequest object with sort that is a property of Customer.
e.g.
Sort sort = new Sort(Sort.Direction.ASC, "c.name");
ordersRepository.search(new PageRequest(x, y, sort));
However, sorting by a field of Order works well:
Sort sort = new Sort(Sort.Direction.ASC, "id");
ordersRepository.search(new PageRequest(x, y, sort));
The error I get is that c is not a property of Order (but since the query is a join of the entities I would expect it to work).
Caused by: org.hibernate.QueryException: could not resolve property c of Order
Do you have any idea how I can sort by a field of the joined entity?
Thank you
In JPA , the thing that you sort with must be something that is returned in the select statement, you can't sort with a property that is not returned
You got the error because the relationship is not modeled properly. In your case it is a ManyToOne relation. I can recomend the wikibooks to read further.
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#ManyToOne
#JoinColumn(name="customer_id", referencedColumnName = "id")
private Customer customer;
}
The query is not needed anymore because the customer will be fetched.
#Repository
public interface OrdersRepository extends PagingAndSortingRepository<Order, Long> {
}
Now you can use nested properties.
Sort sort = new Sort(Sort.Direction.ASC, "customer.name");
ordersRepository.findAll(new PageRequest(x, y, sort));
hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work
I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.