Hibernate inverse join for composite primary key - java

I am new to hibernate.
I have the following situation,
#Entity(name="A")
Class A {
#Id
int id;
Set<B> listB;
}
#Entity(name="B")
Class B {
#Id
CompositebPK bPk;
}
Class CompositebPK {
#Column(name="id")
int aId;
#Column(name="user_name")
String uname;
}
#Entity (name = "user")
Class User {
#Column (name ="user_name")
String uname;
}
table Structures is as follows,
Table A:
int id
Table B:
int id
String uname
User:
String uname
String password
A to B is 1 to many relation.
B to User is 1 to many.
I want to have the list of B's in A.
What is the best approach to solve the situation?
In the net when I search I am just getting information for inverse join for simple columns, if I try that its not working out, so is there a special way to achieve this?

Try the following mapping configuration:
In the A entity class:
#Entity(name="A")
class A {
#Id
int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "bPk.a")
#Cascade(value = { CascadeType.DELETE })
Set<B> listB;
}
In CompositebPk:
class CompositebPK {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(referencedColumnName = "id", nullable = false) })
A a;
#Column(name="user_name")
String uname;
}

Related

How to save two related objects to data base with relation OneToOne

When I'm trying to save an U object I got next exception:
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]
I have two tables:
1. user that columns are id(auto incr, primary), name.
2. contact that columns are id, user_id(that is foreign key -> user.id) and address.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
// getters and setters
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U(){};
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
uRepository.save(user);
}
}
You have set the cascade correctly however because the relationship is bi-directional you need to set both sides in the in-memory model.
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
//will work when this is added
a.setUser(user);
uRepository.save(user);
}
Otherwise, as the error states, A has a null reference for user on save.
Edit: To save using a single repository save call.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U() {
};
// method to manage the bidirectional association
public U addToA(A a) {
this.a.add(a);
a.setUser(this);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U();
user.addToA(new A("address"));
user.setUserName("username");
uRepository.save(user);
}
}
Also, you refer to this link.
inserting values into multiple tables using hibernate
You have to save A first, Then set saved A to U and save U.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Autowired
private ARepository aRepository;
#Test
#Trascational
public void simpleCrudTest() {
A a = new A();
a.setAddress("address");
a = aRepository.save(a);
U user = new U("name", a);
uRepository.save(user);
}
}

JPA Join Query using #Query in repository

I have two tables with composite primary keys and also it has foreign key relation ship. I am trying JPA mappings between these tables. We need to get the below join query result from JPA repository
select * from A i inner join B n on i.id = n.id where i.id = 'XXX' and i.version=99999;
Table: A fields are
id, name, version are primary keys
Table: B fields are
id, name, version, type are primary keys id, name, version, are foreign keys
#Table(name = "A")
#IdClass(APK.class)
public class A {
#Id
#Column(name = "ID")
private String id;
#Id
#Column(name = "NAME")
private String name;
#Id
#Column(name = "Version")
private String version;
//getter setter, toString, equals and hash code
#OneToOne #JoinColumn(name="ID")
private B b;
getter setter
}
public class APK implements Serializable {
private String id;
private Long version;
private String name;
//getter setter, toString, equals and hash code
}
#Table(name = "B")
#IdClass(BPK.class)
public class B {
#Id
#Column(name = "ID")
private String id;
#Id
#Column(name = "NAME")
private String name;
#Id
#Column(name = "Version")
private String version;
#Id
#Column(name = "TYPE")
private Type type;
// getter setter, toString, equals and hash code
#OneToOne #MapsId("ID")
private A a;
//getter setter
}
public class APK implements Serializable {
private String id;
private Long version;
private String name;
private Type type;
//getter setter, toString, equals and hash code
}
Getting the below error with this code:
Caused by: org.hibernate.AnnotationException: A Foreign key refering
com.domain.data.B from com.domain.data.A has the wrong number of
column. should be 5
Please Help on this.
The issue is resolved by using below mapping
#Table(name = "A")
#IdClass(APK.class)
public class A {
#OneToMany(fetch=FetchType.LAZY, mappedBy="a")
private List<B> b;
//getter and setter for b
}
#Table(name = "B")
#IdClass(BPK.class)
public class B {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "id", insertable = false, updatable = false),
#JoinColumn(name = "name", insertable = false, updatable = false),
#JoinColumn(name = "version", insertable = false, updatable = false)
})
private A a;
//getter and setter for a
}
The JPQL query is "select i from B i INNER JOIN i.a n WHERE n.id=:id"

JPA/Hibernate fetch

I have 2 classes with OneToMany one-directional relationship.
class A {
#Column(name = "a_id")
Integer id;
#OneToMany
#JoinColumn(name = "a_id")
private List<B> listOfB;
//getters and setters
}
class B {
Integer id;
String name;
#Column("a_id")
Integer aId;
//getters and setters
}
In database I already have saved instance of B.
I need to do:
a.listOfB.add(b);
save(a);
In object b in database I have id and name, but fk is null. I need to update fk, but before it I need to fetch object b;
How can I do it?
Only by writing custom method, or Hibernate/JPA have its own method to fetch objects?
Your mapping is not very common. Common way to have #JoinColumn in B and mappedBy = "a" in A. And you can specify cascade. In the simplest case cascade = CascadeType.ALL.
class A {
#Column(name = "a_id")
Integer id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
private List<B> listOfB = new ArrayList<B>;
public void addB(B b) {
b.setA(this);
listOfB.add(b);
}
}
class B {
Integer id;
String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
}
To solve your issue
save(a);
b.setA(a);
saveOrUpdate(b);
And cascade will help you to do the same by almost your way with a help of addB() method
a.addB(b);
save(a);

Orders Products and Order_Line Hibernate Relationship with Composite Keys

Ok so today I have spent all day trying to figure out how to map a relationship between Orders, Products, and OrderLine with hibernate using annotations all java config.
I want OrderLine to be a junction table with extra fields, that joins Products and Orders.
Orders has a OneToMany relationsbhip with OrderLine - One order has many order lines.
Each Order has one or more OrderLines, Each OrderLine has one Order, Each OrderLine has one Product, Each Product can have 0 or more OrderLines.
From what I have been following along with tutorials, there are two ways to do it. One being with #Embeddable, #EmbeddedId annotations and the other being #IdClass annotations, I have tried both with no success I will post my code for my shot at #IdClass method.
My orders class
#Entity
#Table(name="orders")
public class Order {
#Id
#Column(name = "order_id")
#OneToMany(fetch = FetchType.EAGER, mappedBy = "order")
private Set<OrderLine> orderLines = new HashSet<>();
...some extra properties relevant to all orders
public Order(){}
...setters/getters
}
My Products class
#Entity
#Table(name="products")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "product_id")
public Integer product_id; // primary key
#OneToMany(fetch = FetchType.EAGER, mappedBy = "product")
private Set<OrderLine> orderLines = new HashSet<>();
...other properties
public Product() {
}
...setters/getters
}
Here is my OrderLine Class
#Entity
#IdClass(OrderLineId.class)
#Table(name="order_line")
public class OrderLine implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "product_id", insertable= false, updatable= false)
public Integer product_id;
#Id
#Column(name = "order_id", insertable= false, updatable= false)
public Integer order_id;
#ManyToOne
#JoinColumn(name = "product_id", insertable = false, updatable = false)
private Product product;
#ManyToOne
#JoinColumn(name = "order_id", insertable = false, updatable = false)
private Order order;
#Column(name = "product_quantity", unique = false, nullable = false)
private int productQuantity;
...additional fields associated with each OrderLine
And finally my OrderLineId implementation
public class OrderLineId implements Serializable {
private static final long serialVersionUID = 1L;
private Integer order_id;
private Integer product_id;
public OrderLineId(){}
public OrderLineId(Integer order_id, Integer product_id) {
this.order_id = order_id;
this.product_id = product_id;
}
#Column(name = "order_id")
public Integer getOrder() {
return this.order_id;
}
public void setOrder(Integer order_id) {
this.order_id = order_id;
}
#Column(name = "product_id")
public Integer getProduct() {
return this.product_id;
}
public void setProduct(Integer product_id) {
this.product_id = product_id;
}
#Override
public int hashCode() {
return order_id + product_id;
}
#Override
public boolean equals(Object obj) {
if(obj instanceof OrderLineId){
OrderLineId orderLineId = (OrderLineId) obj;
return orderLineId.order_id == order_id && orderLineId.product_id == product_id;
}
return false;
}
}
Here is the exception I am getting
MySQLSyntaxErrorException: Unknown column 'orderlines0_.order' in 'field list'
when I visit the end point below
#RequestMapping(value = "/testorder", method = RequestMethod.GET)
public ModelAndView testOrder(){
Order order = orderService.findOrderById(23);
Product product = productService.findProduct(2);
OrderLine ol = new OrderLine(product, order);
ol.setSize("large");
ol.setProductQuantity(30);
orderService.saveOrderLine(ol);
return null;
}
Please can somebody help me, this is driving me crazy....
Thank you
After going through your question and code, I think you have got your design wrong.
You say
want OrderLine to be a junction table with extra fields, that joins Products and Orders.
However you have only two variables, Order and Product in your OrderLine Class.
If I am not wrong, What you really need is a many-to-many table.
Your table order_line would contain two columns order_id and product_id, having foreign key to table orders and products respectively.
Your Order class would be:
#Entity
#Table(name="orders")
public class Order {
#Id
//id of table Order
#ManyToMany(
targetEntity = Product.class,
cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
#JoinTable(name = "order_line",
joinColumns =
#JoinColumn(name = "order_id", nullable = false, updatable = false),
inverseJoinColumns =
#JoinColumn(name = "product_id", nullable = false, updatable = false))
public Set<Product> products= new HashSet(0);
...some extra properties relevant to all orders
public Order(){}
...setters/getters
}
Your Product Class would look like:
#Entity
#Table(name="products")
public class Product implements Serializable {
#Id
#Column(name = "product_id")
public Integer product_id; // primary key
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "products", targetEntity = Order.class) private Set<Order> order = new HashSet<>();
...other properties
public Product() {
}
...setters/getters
}
As you can see, from Order entity you can get 'products' and from Product entity you can get 'orders'. This should server your purpose. No need to use 'OrderLine' class.
Other major errors in code:
- #Id is used to represent Primary key. You have used #Id multiple times for single class.
- In 'Order' class #Id given to Set, and #Id is used along with #OneToMany, which won't work.
Code provided would help you if, indeed what you need is many-to-many table.
I got it to work on my last try last night by doing this change
in OrderLine I removed the product_id and order_id fields and placed the #Id annotation over both ManyToOne relationships like so
#Entity
#IdClass(OrderLineId.class)
#Table(name="order_line")
public class OrderLine implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "product_id", insertable = false, updatable = false)
private Product product;
#Id
#ManyToOne
#JoinColumn(name = "order_id", insertable = false, updatable = false)
private Order order;
#Column(name = "product_quantity", unique = false, nullable = false)
private int productQuantity;
...additional fields associated with each OrderLine
I did not think this woudl work but correct table with correct values was updated, is this valid?
Thank you
Ok I figured it out - I ended up using embeddable method instead based on this guys excellent tutorial here
He goes through 3 major ways to implement many to many assosiations - a simple junction only table, a junction table (with additional fields) which is the method I needed to implement, and finally a less popular method where the class is mapped as a component.

JPA: Column should be unique inside its parent.

I have 2 tables:
#Entity
public class A implements Serializable {
#Column
#NotNull
#Size(min = 1)
#Pattern(regexp = "[A-Za-z]{1}[A-Za-z0-9_]*")
private String token;
#ManyToOne(optional = true)
#JoinColumn(name = "b_id", referencedColumnName = "id")
private B b;
...
}
#Entity
public class B implements Serializable {
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "b")
private List<A> a;
...
}
The Token in entity A should be UNIQUE within B.
For example there is a Token1 within B1 and also a Token1 within B2, but there can
be a second Token1 inside B1.
I'm using JPA with Hibernate.
How do I achieve this?
By creating a UniqueConstraint on the combination of the foreign key and the token.
On entity A, something akin to the following:
#Table(
name="a",
uniqueConstraints={
#UniqueConstraint(columnNames={"b_id", "token"})
}
)
#Entity
public class A implements Serializable {
// ...
}

Categories