I'm new to hibernate. My problem is that I have an Oracle database. I have a view in the database. Now I want to use hibernate to retrieve data in that view. Is there any possible solutions?
Below Snippet can solve your problem, which has been extracted from the tutorial: Mapping Hibernate Entities to Views
Database Query
CREATE OR REPLACE VIEW cameron AS
SELECT last_name AS surname
FROM author
WHERE first_name = 'Cameron';
view entity
#Entity
#NamedNativeQuery(name = "findUniqueCameronsInOrder", query = "select * from cameron order by surname", resultClass = Cameron.class)
public class Cameron implements java.io.Serializable {
private static final long serialVersionUID = 8765016103450361311L;
private String surname;
#Id
#Column(name = "SURNAME", nullable = false, length = 50)
public String getSurname() {
return surname;
}
public void setSurname(final String surname) {
this.surname = surname;
}
}
Hibernate mapping file.
<mapping class="examples.hibernate.spring.query.domain.Cameron" />
finally some test !...
#Test
public void findTheCameronsInTheView() throws Exception {
final List<Cameron> camerons = findUniqueCameronsInOrder();
assertEquals(2, camerons.size());
final Cameron judd = camerons.get(0);
final Cameron mcKenzie = camerons.get(1);
assertEquals("Judd", judd.getSurname());
assertEquals("McKenzie", mcKenzie.getSurname());
}
A view is from accessing data nothing different from table, a problem arises when you want to add,update or delete from view.
Please read http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/querysql.html
It' very similar to mapping ordinary database table.
Create an Entity and use your view name as Table name.
#Entity
#Table(name = "rc_latest_offer_details_view")
public class OfferLatestDetailsViewEntity {
#Id
#Column(name = "FK_OFFER_ID")
private int offerId;
#Column(name = "MAX_CHANGED_DTM")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime changedDateTime;
private BigDecimal price;
...
}
Then query for entities same way as you do for normal table.
Working in Hibernate 4, Spring 4.
we can achieve this by using # Immutable annotation in entity class to map database view with Hibernate
For example : I have created one database view user_data which have 2 columns (id and name) and mapped user_data view in the same way as database tables.
#Entity
#Table(name = "user_data")
#Immutable
public class UserView {
#Id
#Column(name = "ID")
private int ID ;
#Column(name = "NAME")
private String name ;
}
Related
I'm trying to get a single data from two tables of database. These tables doesn't have foreign keys and no jointables too. I'm using spring-data to retrieve required data for first data set.
I have two data sets that have a common String value, and want to retrieve data from both tables not using jointables or foreign keys, retrieving data from the first data set.
I'm using simple DataRepository interface
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataRepository extends JpaRepository<FirstData, Long> {
DataService getById(Long id);
}
FirstData entity:
#Data
#Entity
#Table(schema = "someschema", name = "firstdata")
public class FirstData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "name")
private String name;
#Column(name = "type")
private String type;
}
SecondData entity:
#Data
#Entity
#Table(schema = "someschema", name = "seconddata")
public class SecondData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "type")
private String type;
#Column(name = "value")
private String value;
}
and DataService
#Service
public class DataService {
private DataRepository dataRepository;
public DataService(DataRepository dataRepository){
this.dataRepository = dataRepository;
}
public void getBothFirstAndSecondData() {
List<FirstData> firstDataSet = dataRepository.findAll();
}
}
I need to get data from both tables, but don't want to modify table structure, make jointable or add foreign keys. Also, i don't want to add another repository write code arount second data set. I need just to have a "value" from second data set at first data set result. What is the simpliest approach for solving such data retrieveing?
sql query the above problem can be:
select value from seconddata where id IN (select id from firstdata)
I am using Spring-Boot with JPA and a MySQL backend. Now I got quite confused about the repositories Spring-Boot provides. I know these are quite powerful (and seem to be quite useful since they can shorten your code a lot). Still, I do not understand how to represent Joins within them, since the result-set should be a combination of specified attributes in the select of a few Entities.
Now let's assume we have three tables Book, Author, AuthorOfBook, where the last one is simply connecting Book and Author by a combined Primary key. I guess we had the following Java-Classes:
Entity Book:
#Entity
#Table(name="BOOK")
public class Book {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "TITLE")
private String title;
}
Entity Author
#Entity
#Table(name="AUTHOR")
public class Author {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "LASTNAME")
private String lastname;
#Column(name = "FIRSTNAME")
private String firstname;
//Let's assume some getters and setters and a constructor
}
Entity AuthorOfBook:
#Entity
#Table(name="BOOK")
public class Book {
#EmbeddedId
private AuthorOfBookId pk;
}
An Embedded ID
#Embeddable
public class AuthorOfBookId implements Serializable {
private int authorId;
private int bookId;
}
Repository
#Repository
public interface AuthorOfBookRepository extends JpaRepository<,AuthorOfBookId> {
}
Now how would I represent that query:
SELECT b.name, a.firstname, a.lastname from AuthorOfBook ab inner join Book b on b.id = ab.book_id inner join Author a on a.id = ab.author_id where a.lastname = :lastname;
in my repository? I know the signature would need to be like
#Query([the query string from above])
public (...) findAuthorAndBookByAuthorLastname(#Param("lastname") String lastname);
but I cannot make out what Type the return would be like. What is that method returning? (simply AuthorOfBook would not work I guess)
You don't want AuthorOfBook as a separate Entity. Book should have a field of type Author as a #ManyToOne relationship. That way, given any Book, you can find the author's details.
If you want to handle audits fields you can do something like this:
Audit class
#Embeddable
public class Audit {
#Column(name = "created_on")
private Timestamp createdOn;
#Column(name = "updated_on")
private Timestamp updatedOn;
#Column(name = "is_deleted")
private Boolean isDeleted;
//getters and setters
}
AuditListener to update automatically audits fields
public class AuditListener {
private Long loggedUser = 1001L;
/**
* Method to set the fields createdOn, and isDeleted when an entity is persisted
* #param auditable
*/
#PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if (audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setIsDeleted(Boolean.FALSE);
audit.setCreatedOn(Timestamp.from(Instant.now()));
}
/**
* Method to set the fields updatedOn and updatedBy when an entity is updated
* #param auditable
*/
#PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(Timestamp.from(Instant.now()));
}
}
And add this to the entities
#EntityListeners(AuditListener.class)
public class Book implements Auditable {
#Embedded
private Audit audit;
I have a SQL query like this:
SELECT h.name, h.created_date, tbl.*
FROM my_table tbl
LEFT JOIN
(SELECT name, max(created_date) created_date FROM my_table GROUP BY name) h
ON tbl.name = h.name;
It returns the row from my_table (which has multiple for name="") along with the maximum created_date for that name.
Is there a way to replicate this in a JPQL query?
Here is the gist of the Entity class, it's quite simple:
#Entity
#Table(name = "MY_TABLE")
#XmlRootElement
public class MyTable implements Serializable {
private BigDecimal tableId;
private String name;
private Date createdDate;
// ...
#Id
#Basic(optional = false)
#Column(name = "TABLE_ID")
#GeneratedValue(generator = "TBL_ID_SEQ")
public BigDecimal getTableId() {
return tableId;
}
#Basic(optional = false)
#Column(name = "NAME")
public String getName() {
return name;
}
#Basic(optional = false)
#Column(name = "CREATED_DATE", insertable = false)
#Temporal(TemporalType.TIMESTAMP)
public Date getCreatedDate() {
return createdDate;
}
// ... getters/setters
}
Just reading your question I guess you do not need another entity. Entities in JPA are the same like tables in SQL. Usually there is a 1:1 relationship between entities and tables. You just have to know how to invoke a query using JPQ. You need a entity manager, which invokes your statement.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnit");
EntityManager em = emf.createEntityManager();
You have to define your persistence unit, i.e. in a pom file, or a config java file. So done you can go on coding something like this:
Query q = em.createQuery( "Your query in sql syntax as a string object" );
In respect to your entities and invoked query you will receive a List using
List<object> resultOfMyQuery = q.getResultList();
This is only one short example. But hopefully you got some buzzwords to look for ;)
I have a (simplified) table structure that looks something like that:
customer table:
id name
-------------------
1 customer1
alias table:
customer_id alias
-------------------------------
1 customer one
1 customer uno
When I run the following query I easily get the list of aliases per customer:
select * from customer_alias where customer_id=1;
I would like to use this query in my hibernate to populate a list of type String. I tried using #Formula as follows:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#Column(name = "id")
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Formula("(select alias from customer_alias where customer_id = id)")
private List<String> aliases;
// Getters, setters, etc...
}
It didn't work and I got this exception:
Could not determine type for: java.util.List, at table: customer, for columns: [org.hibernate.mapping.Formula( (select alias from customer_alias where customer_id = id) )]
Is there anyway to achieve this? Doesn't have to be with #Formula of course. Any reasonable way would be great.
Here is an SQLFiddle of my example
You could use #ElementCollection for having a List of related aliases without the need to map the whole entity:
#ElementCollection
#CollectionTable(name = "customer_alias", joinColumns = #JoinColumn(name = "customer_id") )
#Column(name = "alias")
private List<String> aliases;
See also:
Difference between #OneToMany and #ElementCollection?
I think you don't want to use OneToMany annotation as the second table is just a list of strings you want to find something more elegant that would not require me to create another entity.
You can use #ElementCollection as below:
#Entity
#Table(name="college")
class College implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="college_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int collegeId;
#Column(name="name")
private String collegeName;
#ElementCollection
#CollectionTable(name="student", joinColumns=#JoinColumn(name="college_id"))
#Column(name="student_name")
private Set<String> students;
public College() {
}
public Set<String> getStudents() {
return students;
}
public void setStudents(Set<String> students) {
this.students = students;
}
public int getCollegeId() {
return collegeId;
}
public void setCollegeId(int collegeId) {
this.collegeId = collegeId;
}
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
#Override
public String toString() {
return "College [collegeId=" + collegeId + ", collegeName=" + collegeName + ", students=" + students + "]";
}
}
I don't think #Formula annotation supports collection it can only be applied for single-valued properties. Can't say if if there exists any tweak.
I have the following files in Play Framework 2.2.3
Controller:
public class Comment extends Controller
{
public Result create(UUID id)
{
models.blog.Blog blog = models.blog.Blog.finder.byId(id);
Result result;
if(blog == null)
{
result = notFound(main.render("404", error404.render()));
}
else
{
Form<models.blog.Comment> commentForm = Form.form(models.blog.Comment.class);
commentForm = commentForm.bindFromRequest();
if(commentForm.hasErrors())
{
result = badRequest(Json.toJson(commentForm));
}
else
{
models.blog.Comment comment = commentForm.get();
comment.setId(UUID.randomUUID());
comment.setTimeCreated(new Date());
comment.setBlogId(blog.getId());
comment.save();
result = ok(Json.toJson(comment));
}
}
return result;
}
}
And two models
#Entity
#Table(name="blog")
public class Blog extends Model
{
private static final SimpleDateFormat MONTH_LITERAL = new SimpleDateFormat("MMMMM"),
DAY_NUMBER = new SimpleDateFormat("d"),
YEAR_NUMBER = new SimpleDateFormat("yyyy");
public static Finder<UUID, Blog> finder = new Finder<UUID, Blog>(UUID.class, Blog.class);
#Id
#Column(name="id",length=36, nullable=false)
public UUID id;
#OneToOne
#JoinColumn(name="author_id")
public User author;
#Column(name="title",length=255)
public String title;
#Column(name="summary",length=255)
public String summary;
#Column(name="url",length=255)
public String url;
#Column(name="content")
public String content;
#Column(name="time_updated")
public Date time_created;
#Column(name="time_created", nullable=false)
public Date time_updated;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="blog_id")
public List<Comment> comments;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name="blog_tag_map",
joinColumns={ #JoinColumn(name="blog_id", referencedColumnName="id") },
inverseJoinColumns={ #JoinColumn(name="tag_id", referencedColumnName="id") }
)
public List<Tag> tags;
public List<Comment> getComments()
{
return this.comments;
}
}
#Entity
#Table(name="blog_comment")
public class Comment extends Model
{
private static final SimpleDateFormat MONTH_LITERAL = new SimpleDateFormat("MMMMM"),
DAY_NUMBER = new SimpleDateFormat("d"),
YEAR_NUMBER = new SimpleDateFormat("yyyy");
#Id
#Column(name="id",length=36, nullable=false)
public UUID id;
#Column(name="blog_id", length=36)
public UUID blog_id;
#ManyToOne
public Blog blog;
#Column(name="content", length=500)
public String content;
#Column(name="website", length=255)
public String website;
#Column(name="name", length=255)
public String name;
#Column(name="time_created", updatable=false)
public Date time_created;
}
I have excluded some setters and getters from these models for brevity, so it doesn't clog up this post.
When I attempt to make a POST request to the aforementioned controller, everything goes fine until I get to the "comment.save()" statement in the controller file, then I get the following error.
I'm unsure why this save isn't going through, and why there is a column conflict.
Help much appreciated
The issue lies in the fact that you have defined basically two foreign key columns for Blog in your Comment's entity:
#Column(name = "blog_id", length = 36)
public UUID blog_id;
#ManyToOne
public Blog blog;
The default column name for your 'blog' field is: blog_id
However, you've already named your 'blog_id' column that.
Interestingly, no error/warning is thrown when creating this table...
So when you call comment.save(), the following insert statement is generated:
insert into blog_comment (id, blog_id, content, website, name, time_created, blog_id) values (?,?,?,?,?,?,?)
Notice a reference to 'blog_id' column twice, which is invalid.
And this is because of the above double mapping.
To fix, just give your 'blog' property a different name to use for the foreign key column:
#Column(name = "blog_id", length = 36)
public UUID blog_id;
#ManyToOne
#JoinColumn(name = "blogId")
public Blog blog;
I'm not sure why you're mapping your entities like this (perhaps legacy schema?) but the 'blog_id' fields seem to be redundant (and confusing) as you already have an entity mapping in the form of your 'blog' property.
This question is pretty old, but for any future reference i have found this answer that solved my problem.
After numerous searchers around the web I found this answer here - thanks to jtal!
Just to summaries the problem:
Using Ebean i have made a #ManyToOne entity that is not implemented in the database in anyway,
even more the join field, in your case
blogId
is a valid field that has values of its own.
when trying to join the column on that field, it will always fail because it creates this sql query:
SELECT
*
FROM
blog_comment;
select
t0.id c0,
t0.blog_id c1,
t0.content c2,
t0.website c3,
t0.time_created c4,
t0.blog_id c5 <---- notice this duplicate
from
blog_comment t0
in order to solve this, i tell ebean not to use the second set of properties.
your new ebean element should look something like this:
#ManyToOne
#JoinColumn(name = "blogId", insertable = false, updatable = false)
public Blog blog;
hope this helps! =)