I'm using hibernate to create table and then i'm inserting the records in the table when application starts.
For inserting the record i'm using the last example of this page
Problem : Hibernate is able to create the tables but when i'm inserting the records at the application startup it is not getting inserted.
On the other hand if i use a REST service to do the same task it works perfectly fine.
Here is my JPA class.
package com.vizexperts.georbis.usermanagement.types;
import javax.persistence.*;
#Entity
public class TestMe {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long Id;
#Column
String name;
public TestMe(String name) {
this.name = name;
}
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here is corresponding TestMeRepository class.
package com.vizexperts.georbis.usermanagement.types;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface TestMeRepository extends CrudRepository<TestMe, Long> {
TestMe findByName(String name);
}
And this is how i'm inserting the data.
package com.vizexperts.georbis.config;
import com.vizexperts.georbis.usermanagement.types.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class InitialDataConfig implements ApplicationListener<ContextRefreshedEvent>{
#Autowired
TestMeRepository testMeRepository;
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
// Here newly saved object is returned as testMe
// when debugging i can see the auto generated **id** for
// testMe object.
TestMe testMe = testMeRepository.save(new TestMe("Test"));
}
}
And as i said if i use the following service it works.
#RequestMapping("/georbis/test")
public Response<Void> test(){
testMeRepository.save(new TestMe("working"));
return new Response<>(true, "working");
}
I think the reason is that you are missing #Component annotation on your InitialDataConfig class.
And that is why this class is not registered as a bean and onApplicationEvent method is never called.
Add the #Component annotation and it should work fine.
Alternately you can use CommandLineRunner, try this example.
Related
I am trying my hands at Hibernate Relation Mapping(OneToOne, etc) exercises using Spring Boot. Before you ask, I have already consulted this link : [https://stackoverflow.com/questions/11104897/hibernate-attempted-to-assign-id-from-null-one-to-one-property-employee]. I understand that the weak entity needs to have a ref to the parent entity, but I am not able to figure out, why I need to do that explicitly in Person class constructor?
The Codes are as follows.
SpringApplication:
package com.OneToOne.OneToOneMappingPractice;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class App
{
public static void main( String[] args )
{
ApplicationContext applContext = SpringApplication.run(App.class, args);
String[] beanNames = applContext.getBeanDefinitionNames();
Arrays.sort(beanNames);
for(String beanName : beanNames)
System.out.println(beanName);
}
}
CRUDController.java:
package com.OneToOne.OneToOneMappingPractice;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#CrossOrigin
public class CRUDController
{
private static int randomNumber=(int) Math.random();
#Autowired
private CRUDControllerRepository repository;
#GetMapping(value="/add")
public void add()
{
Person person = new Person("Person"+randomNumber, "Street"+randomNumber, randomNumber);
repository.save(person);
randomNumber+=1;
}
#GetMapping(value="/getAll")
public List<Person> getAll()
{
return repository.findAll();
}
#DeleteMapping(value="/deleteAll")
public void deleteAll()
{
repository.deleteAll();
}
}
Person.java:
package com.OneToOne.OneToOneMappingPractice;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
#Entity
public class Person
{
private String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private int Id;
#OneToOne(mappedBy="person", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
public Person() {}
public Person(String name, String streetName, int house_number)
{
super();
this.name = name;
this.address=new Address();
this.address.setStreetName(streetName);
this.address.setHouse_number(house_number);
//this.address.setPerson(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address.java:
package com.OneToOne.OneToOneMappingPractice;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
#Entity
public class Address {
#Id
#Column(name="user_id")
private int Id;
private int house_number;
private String streetName;
#OneToOne
#MapsId
#JoinColumn(name = "user_id")
private Person person;
public Address(){}
public int getHouse_number() {
return house_number;
}
public void setHouse_number(int house_number) {
this.house_number = house_number;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
// public Person getPerson() {
// return person;
// }
public void setPerson(Person person) {
this.person = person;
}
}
CRUDControllerRepository.java:
package com.OneToOne.OneToOneMappingPractice;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
#Repository
#Transactional
public interface CRUDControllerRepository extends JpaRepository<Person, Integer>
{
Person save(Person person);
void deleteAll();
List<Person> findAll();
}
Following are my questions :
As you can see, in the Person class parameterized constructor, I have commented out the line : this.address.setPerson(this);. If I keep this line commented out, I get the exception : "attempted to assign id from null one-to-one property [com.OneToOne.OneToOneMappingPractice.Address.person]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.OneToOne.OneToOneMappingPractice.Address.person]". If I remove the comment syntax, it works perfectly. Why do I need to explicitly do this? Isn't the #OneToOne annotation supposed to take care of these references by itself?
2.If I enable the Person getPerson() method in the Address class, it recursively goes on, until the stack explodes: "Cannot render error page for request [/getAll] and exception [Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException".
Why cant Hibernate itself determine that it needs to stop at that boundary itself, instead of fetching the Parent Object again?
Am I missing something here about these mapping annotations, or anything else?
1- As you can see, in the Person class parameterized constructor, I
have commented out the line : this.address.setPerson(this);. If I keep
this line commented out, I get the exception : "attempted to assign id
from null one-to-one property
Hibernate will not set it explicitly because it does not know to which person this address belongs to you need to specify that explicitly.
The purpose of #OneToOne is to tell hibernate where to get the rest of the data when it is already mapped.
2.If I enable the Person getPerson() method in the Address class, it recursively goes on, until the stack explodes: "Cannot render error
page for request [/getAll] and exception [Could not write JSON:
Infinite recursion (StackOverflowError); nested exception is
com.fasterxml.jackson.databind.JsonMappingException". Why cant
Hibernate itself determine that it needs to stop at that boundary
itself, instead of fetching the Parent Object again?
The exception is caused by Jackson serializer and not from hibernate.
you can look at the examples here to see how it is fixed https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
When creating threads using the Java Callable interface, I am having problems where the properties of an entity I have updated are not synced to the database, however when I do the work in the initial thread, they are. An example I have created below:
package peter.ford.entityupdate;
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.enterprise.concurrent.ManagedExecutorService;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
#Named
#Stateless
public class EntityUpdateBean {
#PersistenceContext(unitName="peter.ford_EntityUpdate_war_1PU")
private EntityManager em;
#Resource
ManagedExecutorService mes;
private String newName;
public String getNewName() {
return newName;
}
public void setNewName(String newName) {
this.newName = newName;
}
public void update() {
UpdateThread t = new UpdateThread();
mes.submit(t);
}
private class UpdateThread implements Callable {
#Override
public Object call() throws Exception {
TypedQuery<Widget> q = em.createQuery("select w from Widget w", Widget.class);
List<Widget> widgets = q.getResultList();
try {
for ( Widget w : widgets) {
w.setName(newName);
}
} catch (Exception e) {
}
return 1;
}
}
}
Class Widget is the entity:
package peter.ford.entityupdate;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Widget implements Serializable {
#Id
#Column(name="ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name="Name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If I change the code so that the work of looking up the entities and then setting the name is done from the update() method of EntityUpdateBean, the changes are updated in the database instantly. No errors appear to be raised, and following the thread in the debugger I can see that it is receiving a list of entities and updating them.
More broadly speaking, is this even the correct approach to update entities from a thread? In this case it is a trivial example, but I have a larger project where I need to update entities in bulk, while checking against data held on a disk for each one, and wish to do this in multiple threads reading from a queue, for performance reasons.
I am trying to find a way I can implement the repository pattern using spring boot with Generic types. So far I looked into this article:
https://thoughts-on-java.org/implementing-the-repository-pattern-with-jpa-and-hibernate/
and tried implementing this solution using generic types based on the solution to this question:
Using generics and jpa EntityManager methods
I attempted to do so using JPA and Hibernate but for me, an error appears when I try returning the class of the entity on the specified type parameter.
the following is my User model using JPA and Hibernate:
package models;
import javax.persistence.*;
#Entity
public class User extends Model {
#Id
#Column(name = "id", updatable = false, nullable = false)
private String id;
public String username;
private String password;
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getPassword() {
return password;
}
}
The following is my interface for basic CRUD operations:
package repositories;
import models.Model;
import java.util.UUID;
public interface IRepository<T> {
void add(T entity);
void delete(String id);
void update(T entity);
T get(String id);
boolean exists(String id);
}
I then created an abstract class for all repositories to avoid repeating myself for all Models.
package repositories;
import models.Model;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
public abstract class Repository<T> implements IRepository<T>{
private EntityManager em;
public Repository(EntityManager em){
this.em = em;
}
#Override
public void add(T entity) {
em.persist(entity);
}
#Override
public void delete(String id) {
T entity = get(id);
em.remove(entity);
}
#Override
public void update(T entity) {
em.merge(entity);
}
#Override
public T get(String id) {
return em.find(getEntityClass(), id);
}
public boolean exists(String id) {
return em.contains(get(id));
}
// link to an explanation can be found at:https://stackoverflow.com/questions/40635734/using-generics-and-jpa-entitymanager-methods
// when a class extends this class, all is needed is to fill out the method body of to return the class.
public abstract Class<T> getEntityClass();
}
the abstract class is there for me to return the class that belongs to T
and this is the specific repository for Users:
package repositories;
import models.Model;
import models.User;
import javax.persistence.EntityManager;
public class UserRepository<User> extends Repository<User> {
public UserRepository(EntityManager em) {
super(em);
}
#Override
public Class<User> getEntityClass() {
return null;
}
}
Ideally, for the getEntityClass method, I would like to return User.class, but I get an error on the IDE saying "Cannot select from type variable". I have looked at a few more questions online and another thing people tried was either put a parameter of type Class or have a member of type Class within the User repository. I tried both methods and it didn't work, any ideas?
class UserRepository<User> should just be class UserRepository. Otherwise, User is just like T, a generic type. Not the class User.
But you're reinventing the wheel. Learn and use Spring Data JPA, which brings generic repositories, and more.
I have made a table persons in a schema name test_schema. I have connected the database from DATABASE > CONNECT TO DATABASE in mysql workbench. Then I tried to connect with that but that doesn't seem to work somehow.
BTW I have cloned it from github and trying to run my database in here.
I have tried to see the status but that shows running but the data is not fetching from the database.
application.yml
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/persons
username: root
password: users
jpa:
hibernate.ddl-auto: null
generate-ddl: true
show-sql: true```
UsersRepository.java
package com.techprimers.db.repository;
import com.techprimers.db.model.Users;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UsersRepository extends JpaRepository<Users, Integer> {
}
Users.java
package com.techprimers.db.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity
public class Users {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "token")
private String token;
#Column(name = "name")
private String name;
#Column(name = "post")
private Integer post;
public Users() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPost() {
return post;
}
public void setPost(Integer post) {
this.post = post;
}
}
UsersResources.java
package com.techprimers.db.resource;
import com.techprimers.db.model.Users;
import com.techprimers.db.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping(value = "/rest/users")
public class UsersResource {
#Autowired
UsersRepository usersRepository;
#GetMapping(value = "/all")
public List<Users> getAll() {
return usersRepository.findAll();
}
}
SpringBootMysqldbApplication.java
package com.techprimers.db;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EnableJpaRepositories(basePackages = "com.techprimers.db.repository")
#SpringBootApplication
public class SpringBootMysqldbApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMysqldbApplication.class, args);
}
}
The result should have been the table elements but instead the Whitelabel Error Page is showing. I don't know what I have been doing wrong since there is nothing much to do. I just modified the code according to my needs but I think I somehow messed with the database connection or I am doing the database connection wrong.
Thanks in advance.
I have come to notice that you said TABLE NAME IS "PERSONS", but in url you have mentioned that as DB name
jdbc:mysql://${DB_SERVER_NAME}:${DB_PORT}/${DB_DATABASE}:currentSchema=${DB_SCHEMA};
which is jdbc:mysql://localhost:3306/<db-name>
Check that once
Unfortunately I can't comment yet but I think your database enforces ssl by default Since MySQL 5.7.5, but it is not obligatory for clients by default. Check this url please jdbc:mysql://localhost:3306/persons?useSSL=false
As Avi mentioned, If your database is test_schema write it in the url instead of persons
And please, provide errors and exceptions right in the question if there any
Link how to handle ssl in mysql
Stackoverflow spring and mysql ssl question
I have been working on spring+hibernate+mysql integration recently.For that, I try to build a basic program that adds, edits, deletes and searches students.I have firstly created model class and added necessary JPA annotations:
package com.joseph.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Student {
#Id
#Column
#GeneratedValue(strategy=GenerationType.AUTO) // for autonumber
private int studentId;
#Column
private String firstname;
#Column
private String lastname;
#Column
private int yearLevel;
public Student(){}
public Student(int studentId, String firstname, String lastname,
int yearLevel) {
super();
this.studentId = studentId;
this.firstname = firstname;
this.lastname = lastname;
this.yearLevel = yearLevel;
}
/* Getters and Setters */
}
Then I constructed DAO interface:
package com.joseph.dao;
import java.util.List;
import com.joseph.model.Student;
public interface StudentDao {
public void add(Student student);
public void edit(Student student);
public void delete(int studentId);
public Student getStudent(int studentId);
public List getAllStudent();
}
To get the necessary data from SQL, I implemented the dao interface as follows:
package com.joseph.dao.impl;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.joseph.dao.StudentDao;
import com.joseph.model.Student;
#Repository
public class StudentDaoImpl implements StudentDao {
#Autowired
private SessionFactory session;
#Override
public void add(Student student) {
session.getCurrentSession().save(student);
}
#Override
public void edit(Student student) {
session.getCurrentSession().update(student);
}
#Override
public void delete(int studentId) {
session.getCurrentSession().delete(getStudent(studentId));
}
#Override
public Student getStudent(int studentId) {
return (Student)session.getCurrentSession().get(Student.class, studentId);
}
#Override
public List getAllStudent() {
return session.getCurrentSession().createQuery("from Student").list();
}
}
But I have error at add(Student student) method at StudentDaoImpl.java class that says:
Multiple markers at this line
- The method add(Student) of type StudentDaoImpl must override or implement a supertype method
- implements com.joseph.dao.StudentDao.add
This error is similar for all other methods in the same class.
How can I fix this?
You should remove the public from the methods on the Interface:
package com.joseph.dao;
import java.util.List;
import com.joseph.model.Student;
public interface StudentDao {
void add(Student student);
void edit(Student student);
void delete(int studentId);
Student getStudent(int studentId);
List getAllStudent();
}
Your code is 100% working, under my test enviroment (InteliJ, JDK7+), so it is connected with your IDE purely.
Try to restart your IDE or reimport this project.
I could share also some tips:
use Long instead of int, when creating model id's
you don't need to add #Column annotation next to #Id, #Id will do alone
inside your parametrized constructor (Student), you don't need to add super(), compiler will add it automatically, seconsly your upper class is Object, so there is no need for that line :)
using such approach would couse lots of code duplication when you will be using many dao, you could use inheritance here, like so:
Here is example of generic DAO class:
public class GenericDaoImpl <D>{
protected SessionFactory session;
protected Class clazz;
public GenericDaoImpl(SessionFactory session, Class clazz){
this.session = session;
this.clazz = clazz;
}
#Override
public void add(D entity) {
session.getCurrentSession().save(entity);
}
#Override
public void update(D entity) {
session.getCurrentSession().update(entity);
}
#Override
public void remove(Long id) {
session.getCurrentSession().delete(get(id));
}
#Override
public Student get(Long id) {
return (Student)session.getCurrentSession().get(clazz, id);
}
#Override
public List getAll() {
return session.getCurrentSession().createQuery("from " + clazz.getSimpleName()).list();
}
}
To create StudentDao, simply do so:
public interface StudentDao{
void add(Student student);
void update(Student student);
void remove(int id);
Student get(int id);
List getAll();
}
And finally your Dao implementation:
public class StudentDaoImpl extends GenericDaoImpl<Student> implements StudentDao{
#Autowired
public StudentDaoImpl(SessionFactory sessionFactory){
super(sessionFactory, Student.class);
}
}
With this approach you could reuse most of this code in all Dao :) So less code to maintain, easy to use.