I want to add a new Ticket to my SQL Database.
Ticket has 2 PK: event and code;
I created Entitys with Composite Keys and want to execute POST requests. I always get "Column 'seller' cannot be null". Anyone here knows how to work with Java Persistence and so on?
I tried it with IdClass and Embeddable but I get errors for both. It's just the inserting of a new Object in the Database. GET Requests work fine...
I tried different annotations but for now nothing worked. I probably don't really understand the concept of Java Persistence, but I also dont have to time to go trough it the whole way. So of it's obvious, please explain a little what it does... Thanks in advance!
My Entities:
Event.java
#Entity
#Table(name="events")
#NamedQuery(name="Event.findAll", query="SELECT e FROM Event e")
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String category;
private String city;
#Temporal(TemporalType.DATE)
private Date date;
#Column(name="image_path")
private String imagePath;
private String name;
private String venue;
//bi-directional one-to-many association to Ticket
#OneToMany(mappedBy="eventBean")
#JsonManagedReference(value="eventBean")
private List<Ticket> tickets;
public Event() {
}
Ticket.java
#Entity
#Table(name="tickets")
#IdClass(TicketPK.class)
//#NamedQuery(name="Ticket.findAll", query="SELECT t FROM Ticket t")
public class Ticket implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private long code;
#Id
private long event;
private float price;
private String type;
//#Column(insertable=false, updatable=false)
//private long event;
//bi-directional many-to-one association to Event
//One Event can have many tickets
#ManyToOne(fetch = FetchType.EAGER)
#JsonBackReference(value="eventBean")
#JoinColumn(name="event",nullable = false,insertable=false,updatable=false)
private Event eventBean;
//bi-directional many-to-one association to User
//One User can have many tickets
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference(value="seller")
#JoinColumn(name="seller", referencedColumnName = "alias")
private User user;
public Ticket() {
}
public Ticket(long event, float price, Event eventBean, User user) {
this.event = event;
this.price = price;
this.eventBean = eventBean;
this.user = user;
}
//getters and setters
public class TicketPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
//#Basic
//#GeneratedValue(strategy = GenerationType.AUTO)
//#Column(name="code", nullable=false)
private long code;
//#Basic
//#Column(name="event",insertable=false)
private long event;
public TicketPK() {
}
public TicketPK(long code, long event) {
this.code = code;
this.event = event;
}
//getters and setters
public boolean equals(Object other) {
if (this.equals(other)) {
return true;
}
if (!(other instanceof TicketPK)) {
return false;
}
TicketPK castOther = (TicketPK)other;
return
(this.code == castOther.code)
&& (this.event == castOther.event);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + ((int) (this.code ^ (this.code >>> 32)));
hash = hash * prime + ((int) (this.event ^ (this.event >>> 32)));
return hash;
}
}
User.java
#Entity
#Table(name="users")
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private String alias;
private String address;
#Column(name="admin_status")
private byte adminStatus;
private String name;
private String password;
#Column(name="phone_number")
private int phoneNumber;
//bi-directional one-to-many association to Ticket
//One User can have many Tickets
#OneToMany(mappedBy="user")
#JsonManagedReference(value="seller")
private List<Ticket> tickets;
public User() {
}
DAO
TicketDAO
public interface TicketDAO extends CrudRepository<Ticket, TicketPK>{
public List<Ticket> findAll();
}
Controller
#Controller
#CrossOrigin
public class MyController {
#Autowired
TicketDAO daoticket;
/****************** OPERATIONS ON Tickets ************************************/
#RequestMapping(value="/tickets", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Ticket> createTicket(#RequestBody Ticket ticket) {
ResponseEntity<Ticket> response;
Ticket newTicket = daoticket.save(ticket);
if (newTicket == null) {
response = new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
} else {
response = new ResponseEntity<>(newTicket, HttpStatus.CREATED);
}
return response;
}
}
SQL Database
CREATE DATABASE IF NOT EXISTS `BD89_03_ticketsell`;
USE `BD89_03_ticketsell`;
DROP TABLE IF EXISTS `TICKETS`;
DROP TABLE IF EXISTS `EVENTS`;
DROP TABLE IF EXISTS `USERS`;
CREATE TABLE `users` (
`name` varchar(100),
`alias` varchar(100) NOT NULL UNIQUE,
`password` varchar(100) NOT NULL,
`address` varchar(100),
`phone_number` int(10),
`admin_status` boolean NOT NULL,
PRIMARY KEY(`alias`)
);
CREATE TABLE `events`(
`id` bigint NOT NULL UNIQUE AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`category` varchar(100) NOT NULL,
`date` date NOT NULL,
`city`varchar(100) NOT NULL,
`venue` varchar(100) NOT NULL,
`image_path` varchar(300) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `tickets`(
`code` bigint NOT NULL,
`event` bigint NOT NULL,
`type` varchar(100),
`price` FLOAT(10,2) NOT NULL,
`seller` varchar(100) NOT NULL,
PRIMARY KEY(`code`, `event`),
FOREIGN KEY(`seller`) REFERENCES `users`(`alias`),
FOREIGN KEY(`event`) REFERENCES `events`(`id`)
);
INSERT INTO events (`name`, `category`, `date`, `city`, `venue`, `image_path`)
VALUES ('Cut Cupy', 'indie dance', '2024-01-22', 'Madrid', 'La Riviera', 'images/CUT-COPY2.png'),
('Dub Inc Tour 2022', 'reggae', '2021-05-03', 'Madrid', 'Sala Sol', 'images/DUB-INC-TOUR.jpg'),
('Kodaline', 'folk', '2022-03-15', 'Sevilla', 'Tablao el gitanillo', 'images/Kodaline.png');
INSERT INTO users (`name`,`alias`,`password`,`address`,`phone_number`, `admin_status`)
VALUES ('pepe', 'pepe', 'password', '17 Calle', '628791051', TRUE),
('pepe2', 'pepe2', 'password', '0 Calle', '628791052', FALSE),
('elisa', 'elisacool', 'contraseña', '3 Calle', '628791053', FALSE);
INSERT INTO tickets (`code`,`event`,`type`,`price`,`seller`)
VALUES ('1234', '1', 'palco', '17.5', 'pepe'),
('666666', '3', 'silla', '20', 'elisacool'),
('666666', '1', 'anfiteatro', '12', 'pepe2'),
('222222', '2', 'palco 3', '21.47', 'elisacool'),
('444666', '3', 'plató', '5', 'pepe'),
('111111', '2', 'palco 2', '40.99', 'elisacool');
Console Error
2022-12-06 15:52:46.186 WARN 9656 --- [io-10303-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000
2022-12-06 15:52:46.186 ERROR 9656 --- [io-10303-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'seller' cannot be null
2022-12-06 15:52:46.186 INFO 9656 --- [io-10303-exec-2] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2022-12-06 15:52:46.199 ERROR 9656 --- [io-10303-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Column 'seller' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916) ~[mysql-connector-j-8.0.31.jar:8.0.31]
.
.
.
I am using spring 4.1.4.RELEASE + hibernate 4.3.6.Final, I am trying #BatchSize for OneToMany, but it seems not working, here is the code:
create table product (
id int(6) unsigned auto_increment primary key,
name varchar(30)
);
create table picture (
id int(6) unsigned auto_increment primary key,
product_id varchar(30),
url varchar(30)
);
#Entity(name = "product")
public class Product extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "product")
#BatchSize(size=2)
private List<Picture> pictures;
public List<Picture> getPictures() {
return pictures;
}
public void setPictures(List<Picture> pictures) {
this.pictures = pictures;
}
}
#Entity(name = "picture")
#BatchSize(size=10)
public class Picture extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "url")
private String url;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id", referencedColumnName = "id")
private Product product;
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
#Repository
public class ProductDao extends AbstractHibernateDao<Product> implements IProductDao {
public ProductDao() {
super();
setClazz(Product.class);
}
#Override
public Product find(final int id) {
Product product = (Product) getCurrentSession().get(clazz, id);
System.out.println("*--------------------find-------------------------");
System.out.println(product.getPictures());
System.out.println("*--------------------end-------------------------");
return product;
}
}
I tried to find Product by id, however the product doesn't contain any pictures inside, I tried to put the BatchSize above the getPictures as well, but it still doesn't work.
I am wondering if I missed some configuration or something, could anyone help?
UPDATE:
Here is the log:
[DEBUG] 2016-10-03 17:20:57.074 RequestMappingHandlerMapping:getHandlerInternal[302]: Returning handler method [public com.lehoolive.analyse.model.IResponse com.lehoolive.analyse.controller.ProductController.detail(int)]
[DEBUG] 2016-10-03 17:20:57.075 DispatcherServlet:doDispatch[931]: Last-Modified value for [/product/detail/1] is: -1
Hibernate: select product0_.id as id2_0_, product0_.name as name2_0_ from product product0_ where product0_.id=?
*--------------------find-------------------------
Hibernate: select pictures0_.product_id as product3_2_1_, pictures0_.id as id1_, pictures0_.id as id1_0_, pictures0_.product_id as product3_1_0_, pictures0_.url as url1_0_ from picture pictures0_ where pictures0_.product_id=?
[com.lehoolive.analyse.entity.Picture#29a0ce34, com.lehoolive.analyse.entity.Picture#5a7a10d8, com.lehoolive.analyse.entity.Picture#3e80350]
*--------------------end-------------------------
[DEBUG] 2016-10-03 17:20:57.333 ResponseBodyAdviceChain:invoke[61]: Invoking ResponseBodyAdvice chain for body=com.lehoolive.analyse.model.Response#59141f65
(From the comments)
There is no way to tell by default JPA that the getPictures() return a limited number of pictures (afaik). In general, I don't think you can limit the number of joined objects returned.
If you want to limit the number of pictures returned by the find method, you have to write your own method (#BatchSize only limits the number of SELECTS statements made, not the number of result).
You can do this with JPA: create a JPQL query on Pictures (not Product), then add .setMaxResult(2) before .getResults() (and you can get your product with youPicturesList().get(0).getProduct(); )
Maybe you can do what you want with the CriteriaBuilder which may allow you to limit on joined entites, but I've never used it like this.
I am starter in Hibernate & Spring MVC and struggling to find correct solution to my problem.
I have Parent Table (Events)and Child table (Votes). I want to save only child table entry whenever data is received from URL. I am having trouble in mapping relations and putting composite key to work
Following is my structure:
create table Events(
Event_ID int(11) NOT NULL AUTO_INCREMENT,
Event_Name varchar(200) NOT NULL,
Event_options int(1) NOT NULL,
Start_TIME timestamp,
End_time timestamp,
Active_Status int(1),
PRIMARY KEY(Event_ID)
)ENGINE=InnoDB AUTO_INCREMENT=16
;
create table Votes(
Event_ID int(11) NOT NULL,
Voter_MSISDN int(13) NOT NULL,
Vote_Option int(1),
PRIMARY KEY(Event_ID,Voter_MSISDN),
FOREIGN KEY (Event_ID) REFERENCES Events(Event_ID)
)ENGINE=InnoDB AUTO_INCREMENT=16
;
Events.java
#Entity
#Table(name="Events")
public class Events {
#Id
#GeneratedValue
#Column(name = "Event_ID")
private Integer eventId;
#Column(name="Event_Name")
private String eventName;
#Column(name="Event_options")
private Integer eventOptions;
#Column(name="Start_TIME")
private String startTime;
#Column(name = "End_time")
private String End_time;
#Column(name="Active")
private Integer status;
#OneToMany(mappedBy = "Events")
private Set<Votes> votes;
//Setter Getters
}
Votes.java
#Entity
#Table(name="Votes")
public class Votes {
public Votes(){}
#EmbeddedId
private Vote vote;
#Column(name = "Vote_Option")
private int Vote_Option;
#ManyToOne
#JoinColumn(name = "Event_ID")
private Events events;
//setters getters
}
Vote.java for Composite primary key setup through #Embeddable
#Embeddable
public class Vote implements Serializable{
public Vote(){}
#Column(name="Event_ID")
private int Event_ID;
#Column(name="Voter_MSISDN")
private long Voter_MSISDN;
//setter getters
}
controller snippet for Adding Event and adding Vote
#RequestMapping(value="/AddEvent")
#ResponseStatus(value = HttpStatus.OK)
public void AddEvent(#RequestParam(value = "ename", required = true) String ename,
#RequestParam (value = "opt") String opt,
#RequestParam (value = "stime") String start,
#RequestParam (value = "etime") String end,
#RequestParam (value = "status") String active){
Events event = new Events();
event.setEventName(ename);
event.setEventOptions(Integer.parseInt(opt));
event.setStartTime(start);
event.setEnd_time(end);
event.setStatus(Integer.parseInt(active));
userDao.saveEvent(event);
}
#Autowired
private Vote vote;
#Autowired
private Votes votes;
#RequestMapping(value="/AddVote")
#ResponseStatus(value = HttpStatus.OK)
public void AddVote(#RequestParam(value = "eventid",required = true) String eventid,
#RequestParam(value="msisdn") String msisdn,
#RequestParam(value = "opt")String opt){
logger.info("Received parameters from URL "+eventid+" "+msisdn+" "+opt);
vote.setEvent_ID(Integer.parseInt(eventid));
vote.setVoter_MSISDN(Long.parseLong(msisdn));
votes.setVote(vote);
votes.setVote_Option(Integer.parseInt(opt));
userDao.saveVotes(votes);
}
}
DAOImplementation:
#Transactional
public void saveEvent(Events event) {
// TODO Auto-generated method stub
Session session = sessionFactory.getCurrentSession();
session.save(event);
}
#Transactional
public void saveVotes(Votes votes){
Session session = sessionFactory.getCurrentSession();
session.save(votes);
}
The code is working fine whenever Event data is received and Event entry is added.
Not able to code correctly for Vote data.
I want to insert just vote data whenever it is received from url. I have added Composite primary key to make sure unique entry for each event from one user(MSISDN).
Please suggest correct mapping for this model.
Any improvement suggestions are also welcome.
First of all you don't need a composite key for this simple solution.
You have a class:
#Entity
#Table(name="Events")
public class Events {
//....................
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Events")
private Set<Votes> votes = new HashSet<>();
}
Simple just create a method in it which would create a new vote for that event. For e.g.:
public Vote createVote() {
Vote vote = new Vote();
vote.setEvent(this);
votes.add(vote);
return vote;
}
Then in your addVote controller method:
#RequestMapping(value="/AddVote")
#ResponseStatus(value = HttpStatus.OK)
public void AddVote(#RequestParam(value = "eventid",required = true) String eventid,
#RequestParam(value="msisdn") String msisdn,
#RequestParam(value = "opt")String opt){
Event event = userDao.findEvent(eventid);
Vote vote = event.createVote(); //This will create a vote for an event.
vote.set.... //set your stuff.
//It will cascade your vote to an event if you have a cascade sorted correctly as in the example above: cascade = CascadeType.ALL
}
Just make sure you got your transactions right. That's just an idea how it should be done.
I'm currently struggling with a mapping of one to many using annotations in hibernate, whenever I get an object, the set of items returned from the related table is always null (even when I can see in the database there are corresponding relationships with data). I have some many to one relationships in the same class which are working fine.
Other unrelated code omitted for readability
I have two tables, where a single member, can have 0 or more member membership periods:
CREATE TABLE member (
member_id INT NOT NULL AUTO_INCREMENT
PRIMARY KEY (member_id)
)
CREATE TABLE member_membership_period (
member_membership_period_id INT NOT NULL AUTO_INCREMENT ,
member_id INT NOT NULL ,
test_column VARCHAR(45) NOT NULL ,
PRIMARY KEY (member_membership_period_id) ,
INDEX member_membership_period_member_idx (member_id ASC) ,
CONSTRAINT member_membership_period_member
FOREIGN KEY (member_id)
REFERENCES member (member_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
The Member class maps to the member table:
#Entity
#Table(name="member")
public class Member implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "member_id")
private int id;
#OneToMany
#JoinColumn(name = "member_id")
#ForeignKey(name = "member_membership_period_member")
private Set<MemberMembershipPeriod> memberMembershipPeriods = new HashSet<MemberMembershipPeriod>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<MemberMembershipPeriod> getMemberMembershipPeriods() {
return memberMembershipPeriods;
}
public void setMemberMembershipPeriods(Set<MemberMembershipPeriod> memberMembershipPeriods) {
this.memberMembershipPeriods = memberMembershipPeriods;
}
}
And the MemberMembershipPeriod class maps to the member_membership_period table
#Entity
#Table(name="member_membership_period")
public class MemberMembershipPeriod implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "member_membership_period_id")
private int id;
#Column(name = "test_column")
String testColumn;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTestColumn() {
return testColumn;
}
public void setTestColumn(String testColumn) {
this.testColumn = testColumn;
}
}
My DAO
public class MemberDaoImpl extends AbstractDAO<Member> implements MemberDao {
public MemberDaoImpl(SessionFactory factory) {
super(factory);
}
#Override
public List<Member> getAllMembers() {
Query query = currentSession().createQuery("from Member");
return list(query);
}
#Override
public Member getMemberById(int id) {
return get(id);
}
}
Implementation of get(id) (Part of drop wizards hibernate package)
protected E get(Serializable id) {
return (E) currentSession().get(entityClass, checkNotNull(id));
}
Any help provided would be greatly appreciated, I'm starting to lose the will to live over this!
Other tech being used is DropWizard (which does the hibernate configuration) and MySQL
Try this. It may help you.
#Override
public List<Member> getAllMembers() {
Criteria criteria = currentSession().createCriteria(Member.class,"member");
criteria.createAlias("member.memberMembershipPeriods","period");
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return criteria.list();
}
I am trying to map the following table
CREATE TABLE Person (
p_id varchar(255) not null,
p_name varchar(255 not null,
p_post_code varchar(12) not null,
primary key (p_id, p_name),
);
Usually when i map an Entity to the above table i would do something like this (for single column primary keys):
private int p_id;
private String p_name;
private String p_post_code;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id")
public Long getPId() {
return p_id;
}
public void setPId(int p_id) {
this.p_id = p_id;
}
#Column(name="p_name")
public String getPname() {
return p_name;
}
public void setPname(String string) {
this.p_name = string;
}
#Column(name="p_post_code")
public String getPostCode() {
return p_post_code;
}
public void setPostCode(String string) {
this.p_post_code = string;
}
The above works if the primary key is a single column (i.e. p_id) and the value for this column is generated in the database.
How would i modify the above to map it so that both p_id and p_name are the primary key.
Also, how would this work, if the composite key is a foreign key in another table.
I am trying to google for some examples but i cant find a simple example and most seem to be using the XML based configuration.
When using composite keys with JPA you need to use an embedded class as an id.
In your case you would have a person class and a primary key class for person:
#entity
public class Person
{
#EmbeddedId
private PersonPK key;
#Column(name="p_post_code", nullable = false)
private String p_post_code;
//....
}
#Embeddable
public class PersonPK
{
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id");
private Long p_id;
#Column(name="p_name")
private String p_name;
public PersonPK(String name)
{
p_name = name;
}
//....
}
Using a class for the person's name (so the name is also a foreign key):
#entity
public class Person
{
#EmbeddedId
private PersonPK key;
#MapsId(value="p_name_id")
#ManyToOne
#JoinColumn(name = "p_name_id", referencedColumnName = "id")
private Name p_name;
#Column(name="p_post_code", nullable = false)
private String p_post_code;
//....
}
#Embeddable
public class PersonPK
{
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id");
private Long p_id;
#Column(name="p_name_id")
private Long p_name_id;
public PersonPK(Name name)
{
p_name_id = name.getId();
}
//....
}
#Entity
public class Name
{
#Id
#GeneratedValue(some generation strategy here)
#Column(name="id")
private Long id;
#Column(name="name")
private String name;
//....
}