I have two tables called STUDENT(studentId,studentName,courseId),COURSE(courseId,courseName) in mysql.I have two .java files for mentioned tables as:
Student.java
package com.vaannila.course;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.*;
#Entity
#Table(name="STUDENT")
public class Student {
private long courseId;
private String studentName;
private long studentId;
public Student() {
}
#Id
#Column(name="studentId")
public long getStudentId() {
return this.studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
#Column(name="studentName", nullable=false)
public String getStudentName() {
return this.studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
#Column(name="courseId")
public long getCourseIdStu() {
return this.courseId;
}
public void setCourseIdStu(long courseId) {
this.courseId = courseId;
}
}
Course.java
package com.vaannila.course;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="COURSE")
public class Course {
private long courseId;
private String courseName;
public Course() {
}
public Course(String courseName) {
this.courseName = courseName;
}
#Id
#Column(name="courseId")
public long getCourseId() {
return this.courseId;
}
public void setCourseId(long courseId) {
this.courseId = courseId;
}
#Column(name="courseName", nullable=false)
public String getCourseName() {
return this.courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
The hibernate.cfg.xml file has
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/emp</property>
<property name="hibernate.connection.username">root</property>
<property name="connection.password">aktpl</property>
<property name="show.sql" >true</property>
<mapping class="com.vaannila.course.Course"/>
<mapping class="com.vaannila.course.Student"/>
</session-factory>
And my HQL is:
String hql = "SELECT s.studentName,c.courseName FROM Student s,Course c where s.courseId=c.courseId and s.studentName=':sname'"; (sname is the input here which i set using setParameter())
But i'm having this error:
org.hibernate.QueryException: could not resolve property: courseId of: com.vaannila.course.Student [SELECT s.studentName,c.courseName FROM com.vaannila.course.Student s,com.vaannila.course.Course c where s.courseId=c.courseId and s.studentName=':sname']
at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83)
at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:77)
at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1809)
at org.hibernate.hql.internal.ast.tree.FromElementType.getPropertyType(FromElementType.java:313)
at org.hibernate.hql.internal.ast.tree.FromElement.getPropertyType(FromElement.java:485)
at org.hibernate.hql.internal.ast.tree.DotNode.getDataType(DotNode.java:598)
at org.hibernate.hql.internal.ast.tree.DotNode.prepareLhs(DotNode.java:266)
at org.hibernate.hql.internal.ast.tree.DotNode.resolve(DotNode.java:213)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:118)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:114)
at org.hibernate.hql.internal.ast.HqlSqlWalker.resolve(HqlSqlWalker.java:883)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1246)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4252)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:3730)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:1923)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:1848)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:782)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:583)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:287)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:235)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:248)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:183)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:119)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:214)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:192)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1537)
at com.vaannila.course.Main.searchResult(Main.java:184)
at Action.ShowStu.doPost(ShowStu.java:56)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:744)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Thread.java:619)
I want to show a table having attributes 'studentName' along with corresponding 'courseName'.
Please help me out !!! Thank u in advance
The annotated getter in Student is called getCourseIdStu(). This means that the property is named courseIdStu (which is a terriblename, BTW). You must thus use this property name in your HQL.
Moreover, you don't need the single quotes around your parameter. The query should thus be:
select s.studentName, c.courseName from Student s, Course c
where s.courseIdStu = c.courseId and s.studentName = :sname
Note that you missed one important part of Hibernate, which consists in providing an object graph on to of a database, and not just individual objects containing IDs of other objects. You shouldn't have a courseId in Student. Instead, you should have a (many-to-one) association with a Course:
#ManyToOne
#JoinColumn(name = "courseId")
public Course getCourse() {
return this.course;
}
This allows getting a student from the database, and navigating to its course. And the query you had could then be rewritten as:
select s.studentName, c.courseName from Student s
inner join s.course c
where s.studentName = :sname
Related
I have a class User which is shared between the server and the android client. So I don't want to annotate the class with an #id annotation.
What is the best way to deal with this in spring-jpa?
I believe the simplest possible way is to fallback to xml mapping so your domain objects will stay pure.
Spring-boot will notice that you're using this type of mapping and configure hibernate for you out of the box.
Here is a simple example:
package com.stackoverflow.so45264894;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.repository.CrudRepository;
#SpringBootApplication
#EnableJpaRepositories
public class So45264894Application {
public static void main(String[] args) {
SpringApplication.run(So45264894Application.class, args);
}
public static class User {
private Long id;
private String username;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
#Bean
CommandLineRunner run(UserRepository userRepository) {
final User user = new User();
user.setUsername("admin");
return args -> userRepository.save(user);
}
}
interface UserRepository extends CrudRepository<So45264894Application.User, Long> {
}
And mapping from /src/main/resources/com/stackoverflow/so45264894/User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.stackoverflow.so45264894" default-access="field">
<class name="com.stackoverflow.so45264894.So45264894Application$User" table="users" dynamic-update="true">
<id name="id" type="java.lang.Long">
<column name="user_id" sql-type="integer"/>
<generator class="identity"/>
</id>
<property name="username" column="username" unique="true"/>
</class>
</hibernate-mapping>
Output:
Hibernate: drop table users if exists
Hibernate: create table users (user_id integer generated by default as identity, username varchar(255), primary key (user_id))
Hibernate: alter table users add constraint UK_r43af9ap4edm43mmtq01oddj6 unique (username)
Hibernate: insert into users (username, user_id) values (?, ?) // <- this is userRepository.save() call
I have a simple table "employee" defined as:
create table employee (id int, name varchar2(40));
I want to write an application to insert values in it and commit only after all values are inserted. Here is my code:
Employee.java:
package com.empl;
public class Employee {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
EmployeeDAO.java:
package com.empl;
import javax.sql.DataSource;
public interface EmployeeDAO {
public void setDataSource(DataSource dataSource);
public void create(int id, String name);
}
EmployeeJDBCTemplate.java:
package com.empl;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class EmployeeJDBCTemplate {
private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
public DataSource getDataSource() {
return dataSource;
}
public void create(int id, String name) {
String SQL = "insert into employee (id, name) values (?, ?)";
jdbcTemplateObject.update(SQL, id, name);
}
}
MainApp.java:
package com.empl;
import java.sql.SQLException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String args[]) throws SQLException {
ApplicationContext context = new
ClassPathXmlApplicationContext("Beans.xml");
EmployeeJDBCTemplate employeeJDBCTemplate =
(EmployeeJDBCTemplate) context.getBean("employeeJDBCTemplate");
employeeJDBCTemplate.getDataSource().getConnection().
setAutoCommit(false);
employeeJDBCTemplate.create(1, "E1");
employeeJDBCTemplate.create(2, "E2");
}
}
Beans.xml:
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<bean id = "dataSource" class =
"org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name = "driverClassName" value =
"oracle.jdbc.driver.OracleDriver"></property>
<property name = "url" value =
"jdbc:oracle:thin:#localhost:1521/xe"></property>
<property name = "username" value = "sys as sysdba"></property>
<property name = "password" value = "pass"></property>
</bean>
<bean id = "employeeJDBCTemplate" class =
"com.empl.EmployeeJDBCTemplate">
<property name = "dataSource" ref = "dataSource"></property>
</bean>
</beans>
I executed the code but when I enter SQLPlus and type the rollback; command, the records are still in the table.
I saw related questions but nothing seems to work.
Basically I need a CRUD application for payments. Each payment is assigned to one account.
My jsp page shows the correct list of "account" objects but it does not set the selected account.
Question: How can I achieve a dropdown box with the assigned account pre-selected?
Question: The assignment (account to payment) works, but only with the below code in my PaymentDaoImpl.java (marked as workaround). Why is this the case?
PaymentDaoImpl.java
..
#Override
#Transactional
public int insertRow(Payment obj) {
// Session session = HibernateUtil.getSessionFactory().openSession();
Session session = sessionFactory.openSession();
// !!!! workaround?? If I don't do this, account won't be assigned
int accountId = obj.getAccount().getId();
Account account = (Account) session.get(Account.class, accountId);
obj.setAccount(account);
Transaction tx = session.beginTransaction();
session.saveOrUpdate(obj);
tx.commit();
Serializable id = session.getIdentifier(obj);
session.close();
return (Integer) id;
}
..
jsp:
<form:select path="account.id" >
<form:option value="-1" label="Select Account" />
<form:options items="${accountList}" itemValue="id" itemLabel="iban" />
</form:select>
Domain Account.java:
package com.beingjavaguys.domain;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.*;
import org.springframework.beans.factory.annotation.Autowired;
#Entity
public class Account {
#Id
#GeneratedValue
private int id;
private String iban;
private String bank;
private String beschreibung;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getIban() {
return iban;
}
public void setIban(String iban) {
this.iban = iban;
}
public String getBank() {
return bank;
}
public void setBank(String bank) {
this.bank = bank;
}
public String getBeschreibung() {
return beschreibung;
}
public void setBeschreibung(String beschreibung) {
this.beschreibung = beschreibung;
}
}
Domain Payment
package com.beingjavaguys.domain;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.springframework.beans.factory.annotation.Autowired;
#Entity
public class Payment {
private int id;
private Account account;
private float amount;
private String text;
private String comment;
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(cascade = CascadeType.ALL, targetEntity = Account.class, fetch=FetchType.EAGER)
#JoinColumn(name="fk_account")
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public float getAmount() {
return amount;
}
public void setAmount(float amount) {
this.amount = amount;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
PaymentController.java
package com.beingjavaguys.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.beingjavaguys.domain.Payment;
import com.beingjavaguys.services.AccountService;
import com.beingjavaguys.services.PaymentService;
#Controller
#RequestMapping("/payment")
public class PaymentController {
#Autowired
PaymentService dataService;
#Autowired
AccountService accountService;
#RequestMapping("form")
public ModelAndView getForm(#ModelAttribute Payment obj) {
ModelAndView mav = new ModelAndView("payment/form");
mav.addObject("accountList", accountService.getList());
return mav;
}
#RequestMapping("insert")
public ModelAndView insert(#ModelAttribute Payment obj) {
dataService.insertRow(obj);
return new ModelAndView("redirect:list");
}
#RequestMapping("list")
public ModelAndView getList() {
List objList = dataService.getList();
return new ModelAndView("payment/list","objList",objList);
}
#RequestMapping("delete")
public ModelAndView deleteUser(#RequestParam int id) {
dataService.deleteRow(id);
return new ModelAndView("redirect:list");
}
#RequestMapping("edit")
public ModelAndView editUser(#RequestParam int id,#ModelAttribute Payment obj) {
ModelAndView mav = new ModelAndView("payment/form");
Payment paymentObj = dataService.getRowById(id);
mav.addObject("accountList", accountService.getList());
mav.addObject("paymentObj", paymentObj);
return mav;
}
#RequestMapping("update")
public ModelAndView updateUser(#ModelAttribute Payment obj) {
dataService.updateRow(obj);
return new ModelAndView("redirect:list");
}
}
Can you have a look on my implementation of the AccountEditor? I need the AccountService to lookup the account, or not? However, I don't get the service instantiated here..
public class AccountEditor extends PropertyEditorSupport {
#Autowired
AccountService dataService; // == null ??
#Override
public void setAsText(String text) {
Account account = lookupAccount(text); // lookup account by accountId
// text
setValue(account);
}
private Account lookupAccount(String text) {
int id = Integer.parseInt(text);
return dataService.getRowById(id);
}
}
What you need is a PropertyEditor implementation to convert from a String accountId to an Account object. Then in your jsp you use the path form:select path="account" instead of form:select path="account.id"
Implement a PropertyEditor as follows
public class AccountEditor extends PropertyEditorSupport{
#Override
public void setAsText(String text){
Account account = lookupAccount(text); //lookup account by accountId text
setValue(account);
}
}
Then register your AccountEditor in your controller
#InitBinder
public void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Account.class , new AccountEditor());
}
With the above changes you wont need your workaround. Your solution is only setting the id property of the account and not the whole object
I'm trying to use Spring Test DBUnit for running an integration test that checks if a DAO's services are running correctly. For two similar entities, I was able to create tests that run OK, but for this particular entity, the test can't run properly.
The test will be ignored, and the only Exception I will see in the console is:
java.lang.IllegalArgumentException: Unable to load dataset from "data/offline_message.xml" using class com.github.springtestdbunit.dataset.FlatXmlDataSetLoader
Here are the relevant files. XML file:
<dataset>
<mp_account id="1" auth_hash="ted.mosby" first_name="Ted" last_name="Mosby" credential="EMAIL" transport_session="someTransportSession"/>
<mp_account id="2" auth_hash="lily.aldrin" first_name="Lily" last_name="Aldrin" credential="MEH" transport_session="someTransportSession"/>
<mp_message id="1" recipient_account_id="1" sender_account_id="2"/>
</dataset>
Test class that is failing:
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.somecompany.messaging.domain.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Date;
import java.util.List;
#DatabaseSetup("data/message.xml")
public class MessageDaoTest extends AbstractDaoTest<Message> {
private static final Long ACCOUNT_ID = 1L;
public static final long DATE_LONG = 1431018764154L;
private static final Date LAST_UPDATE_TS = new Date(DATE_LONG);
#Autowired
MessageDao MessageDao;
#BeforeMethod
public void setUp() throws Exception {
this.setDao(MessageDao);
}
#Test
#Transactional
public void testFindMessages() throws Exception {
List<Message> Messages = this.MessageDao.findMessages(ACCOUNT_ID, LAST_UPDATE_TS);
Assert.assertNotNull(Messages);
Assert.assertEquals(Messages.size(), 1);
}
}
Abstract test class, that extends from TestNG's class:
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
#ContextConfiguration(locations = { "classpath:test-context.xml" })
public class AbstractDaoTest <T> extends AbstractTestNGSpringContextTests {
private GenericDao<T> dao;
#Transactional
public T create(T t){
return dao.create(t);
}
#Transactional
public void delete(Object id){
dao.delete(id);
}
#Transactional
public T find(Object id){
return dao.find(id);
}
#Transactional
public T update(T t){
return dao.update(t);
}
public void setDao(GenericDao<T> dao) {
this.dao = dao;
}
}
Finally, the Entity:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.persistence.*;
#NamedQueries({
#NamedQuery(
name = "findMessagesQuery",
query = "select om from Message om where om.recipientAccount.id=:accountId " +
"and om.lastUpdatedTs>:time and om.lastUpdatedTs+om.expirationPeriod>:now " +
"order by om.lastUpdatedTs asc"
),
#NamedQuery(
name = "findExpiredMessagesQuery",
query = "select om from Message om where om.lastUpdatedTs+om.expirationPeriod<:now"
)
})
#Entity
#Table(name="mp_message")
public class Message extends AbstractModel {
#JoinColumn(name="recipient_account_id", updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Account recipientAccount;
#JoinColumn(name="sender_account_id", updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Account senderAccount;
#Column(name="message_body", length=2048)
private String messageBody;
#Column(name="expiration_period")
private Long expirationPeriod;
// Getters, setters, etc excluded for brevity
}
Just a sidenote: I have "obscured" the Entity name (dammned lawyers!), so there could be some small name mistakes. Please bear with me on this ;)
If you need some additional details, please let me know.
Thanks in advance
try to add classpath: before your path, so:
#DatabaseSetup("classpath:data/message.xml")
Just do this, it works. the relative path can be used.
#DatabaseSetup("/data/message.xml"")
public class App
{
public static void main( String[] args )
{
//test insert
tx = session.beginTransaction();
System.out.println("Maven + Hibernate + MySQL");
// Session session = HibernateUtil.getSessionFactory().openSession();
//session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7000");
stock.setStockName("z");
session.saveOrUpdate(stock);
}
Stock.java
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import com.warp.intercept.*;
/**
* Stock generated by hbm2java
*/
#Entity
#Table(name = "stock", catalog = "mkyong", uniqueConstraints = {
#UniqueConstraint(columnNames = "STOCK_NAME"),
#UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable, IAuditLog {
private Integer stockId;
private String stockCode;
private String stockName;
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
#Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
#Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
I have a java web application, I want to create audit logs in database for all the database changes like(column changed, value changed in column, userid, timestamp and some other fields). I want to create a generalized service which has methods exposed for such operations and I can access them from any other component in the application. As far as I have read, this can be done through hibernate envers or Spring AOP. Can you please suggest and provde me with an example which I can utilize to extend it further.
P.S. : I don't want to use trigger based logging for auditing database changes
Suppose this is my entiity stock; I am performing some simple save operations on the stock thru hibernate. Suppose, I have a main method and I perform below mentioned operation
Well, I am not a Spring or Hibernate user, so I do not know what exactly you need to capture. But if it is just the setters of any #Entity-annotated class, the whole thing would basically look like this:
Driver application:
package de.scrum_master.app;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class Application {
public static void main(String[] args) {
System.out.println("Maven + Hibernate + MySQL");
// Session session = HibernateUtil.getSessionFactory().openSession();
// Transaction tx = session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7000");
stock.setStockName("z");
// session.saveOrUpdate(stock);
}
}
Auditing aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class DBAuditAspect {
#Before("execution(public void set*(*)) && #target(javax.persistence.Entity) && args(setterArgument)")
public void entitySetterExecution(Object setterArgument, JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint + " - " + setterArgument);
}
}
Console output:
Maven + Hibernate + MySQL
execution(void de.scrum_master.app.Stock.setStockCode(String)) - 7000
execution(void de.scrum_master.app.Stock.setStockName(String)) - z
The more general question of how to set up and use Spring AOP is explained in the documentation, chapter 9. Aspect Oriented Programming with Spring.