I have the following Scenatio:
I have a Hibernate Data Entity Class:
public class Home extends BaseDatabaseEntity {
#Id
private long id;
}
that extends my Base Entity:
public abstract class BaseDatabaseEntity {
public static <T extends BaseDatabaseEntity> T getById(Class<T> clazz, Serializable id) {
Transaction transaction = null;
try {
#Cleanup Session session = HibernateConfigurator.getSessionFactory().openSession();
transaction = session.beginTransaction();
T returner = session.get(clazz, id);
transaction.commit();
return returner;
} catch (Exception e) {
...
}
}
}
To retrieve the Object from my Database, i use:
Home.getById(Home.class, 1);
My Goal is to get rid of the Home.class / don't need it in the constructor
Hello, everybody!
Some time ago I run into a trouble: if save method of repository fails, identifier, injected to a bean by Hibernate, remains in the bean. That behaviour may led us to a situation, when we will think about our not persistent bean as about persistent one. I would be pleased to know what practice is common to avoid this situation.
Example test(spring boot + hibernate + oracle database):
#Entity
#SequenceGenerator(name = "TEST_ENTITY_GENERATOR", allocationSize = 1, sequenceName = "TEST_ENTITY_SEQ")
public class TestEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_ENTITY_GENERATOR")
private Long id;
#Column(nullable = false)
private String name;
public Long getId() {
return id;
}
}
#Repository
public interface TestEntityRepository extends JpaRepository<TestEntity, Long> {
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class RemainingIdTest {
#Autowired
private TestEntityRepository testEntityRepository;
#Test
public void test() {
TestEntity entity = new TestEntity();
try {
Assertions.assertThat(entity.getId()).isNull();
testEntityRepository.save(entity);
Assertions.fail("Save must fail");
} catch (DataIntegrityViolationException e) {
Assertions.assertThat(entity.getId()).isNotNull();
}
}
}
A possible solution is to use org.hibernate.event.spi.PreInsertEventListener where we can bind the transaction with a processor that will clear your entity if transaction is failed.
Example:
#Component
public class IdentifierCleaner implements PreInsertEventListener {
#Autowired
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
}
#Override
public boolean onPreInsert(PreInsertEvent event) {
Object entity = event.getEntity();
event.getSession().getActionQueue().registerProcess(((success, session) -> {
if (!success) {
event.getPersister().resetIdentifier(
entity,
event.getId(),
event.getPersister().getVersion(entity),
event.getSession()
);
}
}));
return false;
}
}
Below is the DAO. I am getting the first UppeningUsers object. Note that here for this function I do not want to return peopleWhoBlockedMe set which is located inside the UppeningUsers..
But in different functions I would like to return that information. Note that Both of them are LAZY fetching. With evict I tried to detach the object but still it did not work.
First of all RESTcontroller is below. Then the DAO code is below. Then two entity descriptions are below.
Question is: I see that until
return new ResponseEntity(returned, HttpStatus.OK);
There is only one query which is the typical select. I do not want hibernate to go and take also UserBlock information of that specific UppeningUser. Because it is not needed for this service response. However even though it is lazy loading for some reason
return new ResponseEntity(returned, HttpStatus.OK);
calls the hibernate. I dont know why in restcontroller still it is connected to the database. I tried evict but didnt work.
The json response is
{"id":7,"peopleWhoBlockedMe":[{"blockedId":7}]}
But I do not want for this function to return this peopleWhoBlockedMe. It can be empty.
PLEASE NOTE that in other service for example I will explictly request this peopleWhoBlockedMe but just for this business logic I do not need this information. So what I can do to prevent this so whenever I actually want to call peopleWhoBlockedMe I can get it. Not automaticly.
#RestController
public class TempController {
#Autowired
UppeningUsersService uppeningUsersService;
#RequestMapping(value = "/testing", method = RequestMethod.GET)
public ResponseEntity<UppeningUsers> getPhotos() {
try {
UppeningUsers returned = uppeningUsersService.getUsersDetailsPartial();
return new ResponseEntity<UppeningUsers>(returned, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
This part is the DAO.
#Repository
public class UppeningUsersDAO {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
/**
* Get Existing user. Return error if there is not.
* #param incomingUser user who requested access.
* #return returns the guy information. All information.
*/
#Transactional
public UppeningUsers getUserDetails() throws Exception {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("from UppeningUsers ");
UppeningUsers returning = (UppeningUsers) query.list().get(0);
session.evict(returning);
return returning;
}
}
The main table is this one..
#Entity
#Table(name = "uppening_users")
#Proxy(lazy = true)
public class UppeningUsers {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private
int id;
#OneToMany(mappedBy = "blockedId",cascade =CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserBlocks> peopleWhoBlockedMe;
public UppeningUsers() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<UserBlocks> getPeopleWhoBlockedMe() {
return peopleWhoBlockedMe;
}
public void setPeopleWhoBlockedMe(Set<UserBlocks> peopleWhoBlockedMes) {
this.peopleWhoBlockedMe = peopleWhoBlockedMes;
}
}
Now here is the other table.
#Entity
#Table(name="user_blocks")
#Proxy(lazy = true)
public class UserBlocks {
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
int id;
#Column(name = "blocked_id",insertable = false,updatable = false)
private int blockedId;
public int getBlockedId() {
return blockedId;
}
public void setBlockedId(int blockedId) {
this.blockedId = blockedId;
}
}
UPDATE: 2 forgot to add the service
#Service("uppeningUserService")
public class UppeningUsersService {
#Autowired
UppeningUsersDAO uppeningUsersDAO;
public UppeningUsers getUsersDetailsPartial( ) throws Exception {
return uppeningUsersDAO.getUserDetails();
}
}
Jens is right about her sentence. The layer methodology and writing business objects fix the issue. Thank you.
Image of table relationship reference
After submit from bank jsp page and after submit from card jsp all in one image because of limitation of newbie
I am new to stackoverflow as well SPRING. I have tried to create two tables with foreign key concept . I have followed some examples on stackoverflow as well as from other resourcefull websites and manged to create two tables with onetomany relationship. But the problem is i have to get the first row id under cart_id column when i submit from card jsp page. Instead after submit from card jsp page there is new row created under bankadmin table and it's id is being returned. I am confused and have no idea how to correct ot resolve this issue. Please be kind and guide me. And also i have been searching for a week in stackoverflow couldn't find anything that helped me. Thanks in advance.
Bankadmin Model
#Entity
#Table(name = "bankAdmin")
public class bankAdmin implements Serializable{
#GeneratedValue(strategy=GenerationType.AUTO)
#Column (name = "bcode", nullable=false)
#Id private int bcode;
#Column (name = "bname")
private String bname;
#Column (name = "address")
private String address;
#Column (name = "phno")
private int phno;
#OneToMany(mappedBy="bankAdmin",cascade = CascadeType.ALL)
private Set<Cards> cards;
Card model
#Entity
#Table(name = "cards")
public class Cards implements Serializable {
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="cname", unique=true)
#Id private int cname;
#Column (name = "ctype")
private String ctype;
#Column (name = "min_sal")
private int min_sal;
#Column (name = "year_fee")
private int year_fee;
#Column (name = "rewards")
private String rewards;
#Column (name = "jperks")
private String jperks;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="cart_id", nullable=false)
private bankAdmin bankAdmin;
public Cards(){}
public Cards(String ctype, int min_sal, int year_fee, String rewards, String jperks, bankAdmin b){//int cname,
this.ctype=ctype;
this.min_sal=min_sal;
this.year_fee=year_fee;
this.jperks=jperks;
this.rewards=rewards;
this.bankAdmin=b;
}
public bankAdmin getBankAdmin() {
return bankAdmin;
}
public void setBankAdmin(bankAdmin bankAdmin) {
this.bankAdmin = bankAdmin;
}
CardDaoImpl
public class CardsDaoImpl implements CardsDao{
#Autowired
SessionFactory sessionfactory;
public void save(Cards cards) {
Session session = null;
Transaction tx = null;
try
{
session = this.sessionfactory.openSession();
tx = session.beginTransaction();
bankAdmin bankadmin =new bankAdmin(); //=null;
String _ctype = cards.getctype();
int _min_sal = cards.getmin_sal();
int _year_fee = cards.getyear_fee();
String _rewards = cards.getrewards();
String _jperks = cards.getjperks();
Set<Cards> card = new HashSet<Cards>();
Cards config = new Cards(_ctype,_min_sal,_year_fee,_rewards,_jperks,bankadmin);
card.add(config);
bankadmin.setcards(card);
// System.out.println("bankadmin: before " + bankadmin);
// bankadmin.setbname(bankadmin.getbname());// "SBI"
// bankadmin.setphno(bankadmin.getphno());//1234567890
// bankadmin.setaddress(bankadmin.getaddress());//Bengaluru
// System.out.println("bankadmin: after " + bankadmin);
// int _cname = cards.getcname();
// int bankadmin = bankadmin.getbcode();
//_cname,_ctype,_min_sal,_year_fee,_rewards,_jperks,bankadmin
// card.add(config);
// config.setBankAdmin(cards.getBankAdmin(bankadmin));
// config.setcname(cards.getcname());
// config.setctype(cards.getctype());
// config.setmin_sal(cards.getmin_sal());
// config.setyear_fee(cards.getyear_fee());
// config.setrewards(cards.getrewards());
// config.setjperks(cards.getjperks());
// config.setBankAdmin(cards.getBankAdmin());
session.save(bankadmin);
session.save(config);
tx.commit();
}
catch (HibernateException e)
{
e.printStackTrace();
}
finally
{
session.close();
}
}
// get lms lights config from DB
public List<Cards> Ccards() {
Session session = null;
// Transaction tx = null;
List<Cards> Ccards = null;
try{
session = this.sessionfactory.openSession();
Ccards = session.createQuery("FROM Cards").list();
System.out.println("cards dao impl executed...");
System.out.println("cards config : "+ Ccards.toString());
}
catch (Exception e)
{
System.out.println("bankAdmin Dao impl Ex : " + e);
}
finally
{
session.close();
}
return Ccards;
}
}
BankDaoImpl
public class bankAdminDaoImpl implements bankAdminDao{
#Autowired
SessionFactory sessionfactory;
public void save(bankAdmin badmin) {
Session session = null;
Transaction tx = null;
try
{
session = this.sessionfactory.openSession();
tx = session.beginTransaction();
// bankAdmin bankadmin = new bankAdmin();
bankAdmin config = new bankAdmin();
config.setbcode(badmin.getbcode());
config.setbname(badmin.getbname());
config.setaddress(badmin.getaddress());
config.setphno(badmin.getphno());
session.save(config);//save//persist
tx.commit();
}
catch (HibernateException e)
{
e.printStackTrace();
}
finally
{
session.close();
}
}
// get lms lights config from DB
public List<bankAdmin> BbankAdmin() {
Session session = null;
// Transaction tx = null;
List<bankAdmin> BbankAdmin = null;
try{
session = this.sessionfactory.openSession();
BbankAdmin = session.createQuery("FROM bankAdmin").list();
System.out.println("bankAdmin dao impl executed...");
System.out.println("bankAdmin config : "+ BbankAdmin.toString());
}
catch (Exception e)
{
System.out.println("bankAdmin Dao impl Ex : " + e);
}
finally
{
session.close();
}
return BbankAdmin;
}
}
Okay. I have posted the solution to your problem.
First of all, Spring framework is wonderful to work with. The framework got a lot of features, that you should take advantage of. I am not sure if I will be able to cover everything in this post, so please feel free to ask me.
I have created a simple Spring Boot application. I got total of 6 files that are important which is posted below.
Notice that I renamed your classes to CamelCase with capital starting letter. such as BankAdmin. This is considered the standard way of writing java classes. Also note that i renamed Cards to Card, so remember to rename your table in the database aswell. Also remember to rename the bankadmin table to bank_admin.
There are thee annotations that you have to look into. #Transactional, #Autowired, and PersistenceContext.
So a quick and easy explanation. #Transactional manages all transactions for you, so you do not have to begin and commit transactions. #Autowired creates objects for you, so you do not have to manage your object dependencies yourself. PersistenceContext basically creates and EntityManager for you and manages it for you. You do not have to create session nor EntitManagerFactory. These three annotations are explained very brief, so you should read about them yourself.
I also removed #Table(name = "bankAdmin") and #Table(name = "cards"). JPA can lookup these tables automatically if you follow the standard way of naming classes and database tables. It is actually pretty simple, but I still encourage you to look into this by yourself. In short, capital camelcase is turned into lowercase with _ inbetween each word that start with a capital letter. I.e. If your class name is BankAdmin then JPA will automatically look for table named bank_admin in your database.
application.properties - details about your database
spring.datasource.url=jdbc:mysql://localhost:3306/stackoverflow?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto = update
The below code is only written to test the functionality
#SpringBootApplication
public class StackoverflowApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(StackoverflowApplication.class, args);
//Calling a class that is only made with the purpose of testing
Verification ts = ctx.getBean(Verification.class);
ts.run();
}
}
#Component
class Verification{
#Autowired
private BankAdminDao bad;
#Autowired
private CardsDao cd;
void run(){
//Create a new BankAdmin
BankAdmin ba = new BankAdmin();
ba.setAddress("someStreet");
ba.setPhno(12341234);
ba.setBname("myBanker");
//Create two cards and add them to a HashSet.
Card c1 = new Card("Visa", 1000, 1999, "Alot of", "Babes", ba);
Card c2 = new Card("Master Card", 2000, 500, "someThing", "anotherThing", ba);
Set<Card> cardList = new HashSet<>();
cardList.add(c1);
cardList.add(c2);
//Create a associatio between the BankAdmin and list of Cards
ba.setCards(cardList);
//Save them to the database.
bad.save(ba);
//Here we add a Card to an existing BankAdmin with the id 6 in the database.
//Create a new Card.
//The BankAdmin is set to null, because we not have not yet loaded the BankAdmin
Card c3 = new Card("Visa", 9999, 1337, "Alot of", "Male Babes", null);
//Save Card c3 with the BankAdmin id 6
cd.save(c3, 6);
}
}
BankAdmin
#Entity
public class BankAdmin implements Serializable{
#GeneratedValue(strategy=GenerationType.AUTO)
#Column (name = "bcode", nullable=false)
#Id private int bcode;
#Column (name = "bname")
private String bname;
#Column (name = "address")
private String address;
#Column (name = "phno")
private int phno;
#OneToMany(mappedBy="bankAdmin",cascade=CascadeType.ALL)
private Set<Card> cards;
//Getters and Setters have been removed to reduce the amount of code.
}
BankAdminDao
#Repository
//Transactional makes transaction automatical, so you do not have to begin and commit transactions yourself!
#Transactional
public class BankAdminDao{
//This makes your life a lot eaier!
//It will take care of your EntitManagerFactory and Sessions
#PersistenceContext
EntityManager em;
public void save(BankAdmin bank) {
em.merge(bank);
}
//get lms lights config from DB
public List<BankAdmin> getAllBankAdmin() {
List<BankAdmin> bankList = (List<BankAdmin>)em.createQuery("SELECT b FROM BankAdmin b");
return bankList;
}
public BankAdmin getBankAdmin(int bankId) {
return em.find(BankAdmin.class, bankId);
}
}
Card
#Entity
public class Card implements Serializable {
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="cname", unique=true)
#Id private int cname;
#Column (name = "ctype")
private String ctype;
#Column (name = "min_sal")
private int min_sal;
#Column (name = "year_fee")
private int year_fee;
#Column (name = "rewards")
private String rewards;
#Column (name = "jperks")
private String jperks;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="cart_id", nullable=false)
private BankAdmin bankAdmin;
public Card(){}
public Card(String ctype, int min_sal, int year_fee, String rewards, String jperks, BankAdmin b){
this.ctype=ctype;
this.min_sal=min_sal;
this.year_fee=year_fee;
this.jperks=jperks;
this.rewards=rewards;
this.bankAdmin=b;
}
public BankAdmin getBankAdmin() {
return bankAdmin;
}
public void setBankAdmin(BankAdmin bankAdmin) {
this.bankAdmin = bankAdmin;
}
}
CardDao
#Repository
#Transactional
public class CardsDao{
#PersistenceContext
EntityManager em;
#Autowired
BankAdminDao bad;
public void save(Card cards, int bankId) {
BankAdmin bank = bad.getBankAdmin(bankId);
cards.setBankAdmin(bank);
bank.getCards().add(cards);
em.merge(bank);
}
public List<Card> getAllCards() {
List<Card> cardList = (List<Card>)em.createQuery("SELECT c FROM Cards c");
return cardList;
}
public Card getCard(int cardId){
return em.find(Card.class, cardId);
}
}
I recently came across a situation where I have to do some actions when object is deleted through Hibernate session.
I wan't to remove unidirectionl relashonship before an entity is deleted, but the following code results in a stackoverflow exception.
#Component("emsPreListener")
public class IntegrationEntityDeleteListener implements PreDeleteEventListener {
private static final long serialVersionUID = 2245534615822054792L;
#Override
#SuppressWarnings("unchecked")
public boolean onPreDelete(PreDeleteEvent event) {
System.out.println("PRE-DELETE");
Session session = event.getSession();
if (event.getEntity() instanceof Project) {
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
Project project = (Project) event.getEntity();
Criteria criteria = session.createCriteria(ProjectPoll.class);
criteria.add(Restrictions.eq("project", project));
List<ProjectPoll> polls = criteria.list();
if(polls != null) {
for(ProjectPoll projectPoll : polls) {
session.delete(projectPoll);
}
return false;
}
}
catch (Exception exception) {
exception.printStackTrace();
}
finally
{
if(transaction != null) transaction.commit();
}
}
return false;
}
}
This it the only entity with the relashonship.
#Entity
#Table(name = "project_poll")
#PrimaryKeyJoinColumn(name = "poll_id", referencedColumnName = "id")
public class ProjectPoll extends Poll {
private static final long serialVersionUID = -2230614967405436988L;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "project_id")
private Project project;
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
Tnx
You annotated bidirectional relation ProjectPoll.project with cascade=CascadeType.ALL. Removing a projectPoll will remove also a parent (project). This in turn will trigger onPreDelete() once more. Try to remove cascade attribute on bidirectional relation.