I am using JPA to create and access a database. My database consists of two tables? These two tables have a #OneToMany relationship. However, when I run my application, I am getting a java.sql.SQLException.
I have done some research but none of the solutions online seem to work for me. The first solution suggests to replace #GeneratedValue(strategy=GenerationType.AUTO) with #GeneratedValue(strategy=GenerationType.IDENTITY)
The second solution consists of Including spring.jpa.hibernate.use-new-id-generator-mappings=false into application.properties.
None of these solutions worked.
Car.java
#Entity
public class Car {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
....
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "owner")
private Owner owner;
....
}
Owner.java
#Entity
public class Owner {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long ownerid;
....
#OneToMany(cascade =CascadeType.ALL, mappedBy="owner")
private List<Car> cars;
....
}
application.properties
logging.level.root=INFO
server.port=8080
spring.jpa.show-sql=true
spring.datasource.url=jdbc:mariadb://localhost:3306/cardb
spring.datasource.username=****
spring.datasource.password=*******
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.generate-dll=true
spring.jpa.hibernate.dll-auto=create-drop
Here is the Exception I am getting :
java.sql.SQLException: Table 'cardb.owner' doesn't exist
EDIT: I am using MariaDB and the database's name is cardb. So, when I run the application, it should create cardb.car, cardb.owner and cardb.car_owner
By the way, the database is properly created when using H2. Does it mean the problem comes from MariaDB ?
#JoinColumn(name = "owner")
should be
#JoinColumn(name = "ownerid")
I think you are missing #Table in your entity classes.
Define Car as,
#Entity
#Table (name="car")
public class Car {
...
}
and
Owner as below,
#Entity
#Table (name = "owner")
public class Owner {
...
}
Related
I have a class Project, a superclass Resource with two subclasses StorageResource and ComputeResource. I have defined a Hibernate mapping (I am using Hibernate version 5.3.7.Final) as follows:
#Entity
#Table(name = "PROJECTS", ...})
public class Project implements Serializable {
...
#OneToMany(
mappedBy = "project",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Resource> resources;
...
}
#Entity
#Table(name = "resources")
#Inheritance(strategy = InheritanceType.JOINED)
public class Resource implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#ManyToOne
private Project project;
...
}
#Entity
#Table(name = "storage_resources")
#PrimaryKeyJoinColumn(name = "id")
public class StorageResource extends Resource implements Serializable {
...
}
#Entity
#Table(name = "compute_resources")
#PrimaryKeyJoinColumn(name = "id")
public class ComputeResource extends Resource implements Serializable {
...
}
My Java code can save instances of both subclasses (StorageResource and ComputeResource) correctly to the database. However, when I load these records by loading a project and its resources, I have the following:
Storage resource records are loaded as Java objects that are instances of StorageResource,
Compute resource records are loaded as Java objects that are instances of Resource, not of ComputeResource as I would expect.
I have spent the last two days trying to figure out why this is happening: the mappings of both sub-classes are the same. Am I missing something?
EDIT
I found the problem. It was quite misleading, because there was no error message. The environment in which the bug occurred had an outdated configuration file. The Hibernate mapping for ComputeResource
<mapping class="....ComputeResource"/>
was missing. After adding this line the mapping is working as expected.
When I try to run my Application Class it gives me the following error:
2021-04-26 16:20:47.551 ERROR 14416 --- [ main] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: Unable to find column with logical name: property_id in org.hibernate.mapping.Table(property) and its related supertables and secondary tables
From looking at that it feels to me like I might've named it incorrectly, but even when I went into the table and copied field names directly it didn't fix the issue.
The Property entity is as follows:
#Entity
#Table(name = "property")
public class Property {
#Id
#OneToOne
#JoinColumn(name = "property_id", referencedColumnName = "property_id")
private Address address;
// other fields
With its table structure being:
While the Address entity it's connected to is:
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue
#Column(name = "property_id")
private Long propertyID;
// other fields
With table structure:
As you can see I have the names correct, and I believe my datasource is correctly formatted, if I use a column name that isn't in the table IntelliJ throws a warning saying it cannot resolve the column, which I don't get as they're written now.
This is my first big project using JPA so I might be missing something obvious but any help would be much appreciated, I've looked at other solutions and tried some of the suggestions but they all seem to be slightly different and not applicable to what I'm trying. Any help would be greatly appreciated :)
#Entity
#Table(name = "property")
public class Property {
#Id
private Long id;
#OneToOne
#MapsId
#JoinColumn(name="property_id")
private Address address;
...
}
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue
#Column(name = "property_id")
private Long propertyID;
...
}
See the Hibernate ORM documentation for Derived Identifiers.
I'm working with JPA, Spring boot.
Using #OneToMany annotation, when I fetch orders containing cart items.
My domain codes are below.
Order:
#Data
#Entity
#Table(name="\"order\"")
#ToString
public class Order {
#Id
#GeneratedValue
#Getter #Setter
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="order_id")
#Getter #Setter
private List<Cart> carts;
public void addCart(Cart cart) {
if (this.carts == null) {
carts = new ArrayList<Cart>();
}
carts.add(cart);
}
}
Cart:
#Data
#Entity
#Table(name="cart")
#ToString
public class Cart {
#Id
#GeneratedValue
#Getter #Setter
#Column(name="id")
private Long id;
#Column(name = "order_id")
#Getter #Setter
private Long orderId;
}
This works very well when I fetch only one order, but doesn't work when I fetch more than two orders. I mean when I fetch only one order, carts field's size is 1 or more, but when I fetch two or more orders, the carts field's size is 0.
How can I solve this problem? Thank you in advance!
It's nearly impossible to find out what the fetching problem is without seeing the code which is loading / querying your entities. So could you please add it to your question?
Meanwhile there are at least some things you could improve to have cleaner entities and faster code, maybe this also can help a little to hunt down the problem.
At first, you are using redundant configurations, respective annotations you are recreating the default values with.
I assume that is lombok what you are using, right? You could remove the #Getter and #Setter annotations on your fields in both entities and add it once on class level to avoid the declaration on every single field, but since you are using #Data you don't need it at all. Same with #toString, #Data is a convenience annotations for all of it (and a little more).
The JavaDoc of #Data says:
Equivalent to {#code #Getter #Setter #RequiredArgsConstructor #ToString #EqualsAndHashCode}.
Then, although the #Table(name="\"order\"") on the order entity is needed because order is a reserved word in some DBMS, the #Table(name="cart") on the cart entity is the default.
Up next, I would not recommend lazy initialization of collections, because in general there is no benefit to do that compared to the penalty it causes while checking for null before every access. Just initialize it within declaration and you will never have to care about handling null again. Beside of that you should think about using a Set instead a List for the carts (so you will have no duplicates).
Also think about the FetchType because EAGER is only useful if you work with detached entities and want to initialize the relation in every case. LAZY is the default for #OneToMany and should be preferred.
A thing you already improved is the #JoinColumn, that will prevent Hibernate (I brazenly assume you are using Hibernate) creating a join table. But even more important is thinking about turning the #OneToMany into a #ManyToOne on the other side or making it bidirectional. That would gain some performance on reading (it also prevents the join table so less joins are needed, faster indexing is possible) and writing time (relation managed by parent side).
So what do you think about this:
#Data
#Entity
#Table(name="\"order\"")
public class Order {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Cart> carts = new HashSet<>();
}
and this:
#Data
#Entity
public class Cart {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Order order;
}
Order class:
public class Order {
#Id
#GeneratedValue
#Getter #Setter
private Long id;
#OneToMany(mappedBy="orderId" fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#Getter #Setter
private Set<Cart> carts;
}
Cart class:
public class Cart {
#Id
#GeneratedValue
#Getter #Setter
#Column(name="id")
private Long id;
#ManyToOne
#Getter #Setter
private Order orderId;
}
For example: JPA OneToMany and ManyToOne Relationships
I just wrote it to change.
I hope that it will work
I have a two objects with simple #OneToMany relationship which looks as follows:
parent:
#Entity
public class ParentAccount {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parentAccount")
private Set<LinkedAccount> linkedAccounts;
}
child:
#Entity
public class LinkedAccount {
#Id
#GeneratedValue
private long id;
#ManyToOne(optional = false)
private ParentAccount parentAccount;
private String name;
// empty constructor for JPA
public LinkedAccount() {
}
}
I ma using Spring CrudRepository to operate with these entities. However, when calling ParentAccount parent = parentAccountRepository.findOne(id);, some kind of infinite loop starts happening and hibernate spams this all over the console:
Hibernate: select linkedacco0_.parent_account_id as parent_a6_1_0_, linkedacco0_.id as id1_0_0_, linkedacco0_.id as id1_0_1_, linkedacco0_.aws_id as aws_id2_0_1_, linkedacco0_.key_id as key_id3_0_1_, linkedacco0_.name as name4_0_1_, linkedacco0_.parent_account_id as parent_a6_0_1_, linkedacco0_.secret_key as secret_k5_0_1_ from linked_account linkedacco0_ where linkedacco0_.parent_account_id=?
I tried changed the fetch type to LAZY but then I get this error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.berrycloud.scheduler.model.ParentAccount.linkedAccounts, could not initialize proxy - no Session
(It seems that it is trying to do the lazy load outside of the transactional context).
This is my CRUD repository:
#Repository
public interface ParentAccountRepository extends CrudRepository<ParentAccount, Long> {
}
Could someone tell me how to resolve this issue? I would prefer the solution with EAGER fetch. Thank you for any tips
EDIT: here is the schema I am using
CREATE TABLE parent_account (
id BIGINT auto_increment,
name VARCHAR(80) null,
PRIMARY KEY (`id`)
);
CREATE TABLE linked_account (
id BIGINT auto_increment,
parent_account_id BIGINT,
name VARCHAR(80) null,
FOREIGN KEY (`parent_account_id`) REFERENCES `parent_account` (`id`),
PRIMARY KEY (`id`)
);
As the first answer suggests:
Do not use Lombok's #Data annotation on #Entity classes.
Reason: #Data generates hashcode(), equals() and toString() methods that use the generated getters. Using the getter means of course fetching new data even if the property was marked with FetchType=LAZY.
Somewhere along the way hibernate tries to log the data with toString() and it crashes.
Problem solved. I was using a custom #toString method in the LinkedAccount which was referencing the ParentAccount. I had no idea that this could cause any problem and therefor I did not include the toString in my question.
Apparently, this was causing an infinite loop of lazy loading and removing this reference fixed the problem.
As user1819111 told, #Data from Lombok is not compatible with #Entity and FetchType=LAZY. I had used Lombok.Data (#Data) and I was getting this error.
As I don't want do create all get/set, I just put the Lombok #Setter and #Getter in your class and all will work fine.
#Setter
#Getter
#Entity
#Table(name = "file")
#SequenceGenerator(name = "File_Sequence", allocationSize=1, sequenceName = "file_id_seq")
public class MyClass{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "File_Sequence")
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "file", cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
private Set<Base2FileDetail> details = new HashSet<>();
}
Something like this does not work?
#Entity
public class Account {
#Id
#GeneratedValue
private long id;
private String name;
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="manager_id")
private Account manager;
#OneToMany((fetch = FetchType.EAGER, mappedBy="manager")
private Set<Account> linkedAccounts = new HashSet<Account>();
}
I recently had this issue due to a poorly defined Jackson2HttpMessageConverter.
I had done something like the following.
#Bean
RestTemplate restTemplate(#Qualifier("halJacksonHttpMessageConverter")
TypeConstrainedMappingJackson2HttpMessageConverter halConverter) {
final RestTemplate template = new RestTemplateBuilder().build();
halConverter.setSupportedMediaTypes(List.of(/* some media types */));
final List<HttpMessageConverter<?>> converters = template.getMessageConverters();
converters.add(halConverter);
template.setMessageConverters(converters);
return template;
}
This caused a problem because the media types did not include all the defaults. Changing it to the following fixed the issue for me.
halConverter.setSupportedMediaTypes(
new ImmutableList.Builder<MediaType>()
.addAll(halConverter.getSupportedMediaTypes())
.add(/* my custom media type */)
.build()
);
This simple way worked for me. Just use JsonIgnoreProperties .
#JsonIgnoreProperties(value = {"linkedAccounts"})
#ManyToOne(cascade = { CascadeType.PERSIST})
#JoinColumn(name = "abc", referencedColumnName = "abc")
private ParentAccount parentAccount;
This way worked for me without removing #ToSring annotation:
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "parent_accounts")
public class ParentAccount {
#JsonIgnoreProperties({"parentAccount"})
#OneToMany(mappedBy = "parentAccount",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<LinkedAccount> linkedAcounts;
// ...
}
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "linked_accounts")
public class LinkedAccount {
#JsonIgnoreProperties("linkedAcounts")
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "parentAccount_id")
private ParentAccount parentAccount;
// ...
}
PS: In #JsonIgnoreProperties You can also ignore more than one field for preventing infinite loop
I am trying to get the following type of mapping to work
Table event has the following columns:
id (PK)
prodgroup
errandtype
table errandtype : errandtype
table prodgroup: prodgroup
I have corresponding JPA classes
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
Prodgroup prodgroup;
// what mapping should go here?
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
Ok so questions are marked as comments in the code but I'll try to be explicit anyway.
In the above example I want my Prodgroup and ErrandType fields in the MyEvent class to be set to corresponding Prodgroup and Errandtype instances
I have tried #OneToOne relationships with #joincolumns and with mappedby attribute, but I just can't get it working and I've lost all sense of logical approach. My grasp of JPA entity mapping is clearly weak.
So can anyone bring some clarity?
It should be:
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "prodgroup_id", insertable = true, updatable = true)
Prodgroup prodgroup;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "errandtype_id", insertable = true, updatable = true)
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
FetchType Eager means the object will be always loaded (would be "Lazy" by default if not specified).
CascadeType.ALL means mearge/persist/remove will be also done to linked tables.
Sebastian
Your table columns event.prodgroup and event.errandtype are foreign keys to respective tables (prodgroup, errandtype). So you need #ManyToOne association (because many events may share one prodgroup or errantype).