How to not have to specify Hibernate mapping - java

I have a working hibernate setup using annotations
#Entity
#Table(name="Users")
public class User implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="USER_ID")
private int id = 0;
#Column(name="EMAIL")
private String email = "";
#Temporal(TemporalType.TIMESTAMP)
#Column(name="CREATED")
private Date created = null;
public User(){
Calendar cal = Calendar.getInstance();
this.created = cal.getTime();
}
public User(int id, String email, Date created) {
this.id = id;
this.email = email;
this.created = created;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
#Override
public String toString() {
return "centaurus.hib.User{" +
"id=" + id +
", email='" + email + '\'' +
", created=" + created +
'}';
}
}
to make this work i have to have a entry(amongst others) in my hibernate.cfg.xml file
<mapping class="centaurus.hib.User"/>
Otherwise hibernate throws error saying it has no mapping file.
alternatively when i create my persistent sessionfactory I can specify what classes are to be mapped.
The issue is, on other projects i have worked on I only needed to add a class and annotate it correctly for hibernate to use it. this is what i would like to do but don't know how. I don't want to have a list of classes in my hibernate config in addition to the annotated classes.

If you use Spring for your Session Factory, you can specify the 'packagesToScan' property.
<property name="packagesToScan" value="com.xyz.model" />

You can also use the Hibernate Configuration class if you're wiring up Hibernate manually and call Configuration#addAnnotatedClass(Class<?>).

Related

Unable to fetch results with associations in spring boot 2.5.0

I am trying to upgrade my spring boot project from 2.4.3 to 2.5.0. The strange thing happened to me is I am unable to fetch the results when entities are associated.
For example, I have two simple entities:
UserProfile entity:
#Entity
#Table(name = "user_profile")
public class UserProfile {
#Id
private String id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String email;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "tenantId")
private Tenant tenant;
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Tenant entity:
#Entity
public class Tenant {
#Id
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
UserProfile entity has many to one association with Tenant entity. My repository class is
public interface UserProfileRepository extends CrudRepository<UserProfile, String> {
UserProfile findByEmailAndTenantId(String email, String tenantId);
}
Now, the method findByEmailAndTenantId from the above repository always returns null.
The SQLs generated with spring boot 2.5.0 is
select
userprofil0_.id as id1_1_,
userprofil0_.email as email2_1_,
userprofil0_.first_name as first_na3_1_,
userprofil0_.last_name as last_nam4_1_,
userprofil0_.tenant_id as tenant_i5_1_
from
pr.user_profile userprofil0_
inner join
pr.tenant tenant1_
on userprofil0_.tenant_id=tenant1_.id
where
userprofil0_.email=?
and (
tenant1_.id is null
)
The SQL generated for the same entities with spring boot 2.4.3 is
select
userprofil0_.id as id1_1_,
userprofil0_.email as email2_1_,
userprofil0_.first_name as first_na3_1_,
userprofil0_.last_name as last_nam4_1_,
userprofil0_.tenant_id as tenant_i5_1_
from
pr.user_profile userprofil0_
where
userprofil0_.email=?
and (
userprofil0_.tenant_id is null
)
Is this intended behavior in Spring Boot 2.5.0? Can anyone please help me in finding the solution to this problem?
I couldn't find anything in the documentation about it, but read this What is the difference between #ManyToOne(optional=false) vs. #Column(nullable=false)
It seems that optional = false results in INNER JOIN to tenant, that combined with WHERE tenant_id IS NULL can't return any rows.
So if you really need to fetch entities with tenant_id set to null, you've got to remove optional = false from the #JoinColumn annotation.

CrudRepository save method not saving to oracle databse

I am trying to create an application to save data into the Oracle database using CrudRepository. Here is my repositiry:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByEmail(String email);
List<Customer> findByDate(Date date);
// custom query example and return a stream
#Query("select c from Customer c where c.email = :email")
Stream<Customer> findByEmailReturnStream(#Param("email") String email);
}
My application.property looks like:
spring.datasource.url=jdbc:oracle:thin:#vgdevst-scan.hhs.local:1521/EONDEV.hhslocal
spring.datasource.username=EON_USER
spring.datasource.password=EON_USERD
spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver
While my customer entity class is :
#Entity
public class Customer {
//http://www.oracle.com/technetwork/middleware/ias/id-generation-083058.html
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUST_SEQ")
#SequenceGenerator(sequenceName = "customer_seq", initialValue = 1, allocationSize = 1, name = "CUST_SEQ")
Long id;
String name;
String email;
//#Temporal(TemporalType.DATE)
#Column(name = "CREATED_DATE")
Date date;
public Customer(String name, String email, Date date) {
this.name = name;
this.email = email;
this.date = date;
}
public Customer(Long id, String name, String email, Date date) {
super();
this.id = id;
this.name = name;
this.email = email;
this.date = date;
}
public Customer() {
}
#Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", date=" + date +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
I am trying to save a new cutomer to database using:
#SpringBootApplication
public class Application implements CommandLineRunner {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
#Autowired
DataSource dataSource;
#Autowired
CustomerRepository customerRepository;
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Transactional(readOnly = true)
#Override
public void run(String... args) throws Exception {
System.out.println("DATASOURCE = " + dataSource);
customerRepository.save(new Customer(new Long(4),"Amit","a.r#state.ma.us",new Date()));
System.out.println("\n1.findAll()...");
for (Customer customer : customerRepository.findAll()) {
System.out.println(customer);
}
}
}
I do not see the new customer added either in sops or in database. What am i missing here?
Your problem seems to be that you are executing the save statement in a readOnly transaction. The solution could be as simple as removing that property.
Reading the readOnly flag documentation, it states that:
A boolean flag that can be set to true if the transaction is effectively read-only, allowing for corresponding optimizations at runtime.
Use only #Transactional:
#Transactional
#Override
public void run(String... args) throws Exception {
// the rest of your code ...
}
The code was working just fine.
Its just that in my application code, i had changed the application.property file as per my old code and instead of "spring.datasource.url" i had put "appname.datasource.url", which is why code never interacted with DB.

Boolean field Hibernate QueryException: could not resolve property

I am really newbie to Hibernate and it's been like two hours trying to figure it out how to fix this issue. I am using Hibernate 4 and Postgres 9.3
Given the CatalogBase class
#MappedSuperclass
public class CatalogBase {
#Id
#Type(type = "pg-uuid")
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
protected UUID id;
}
And the derived User class
#Entity
#Table(name="erpuser")
public class User extends CatalogBase {
private String lastName;
private String name;
private String email;
private boolean isSystemAdministrator;
#Type(type="org.hibernate.type.StringClobType")
#Column(nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="lastname")
#Type(type="org.hibernate.type.StringClobType")
#NotNull(message = "es campo mandatorio")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(length = 100,unique = true)
#NotNull(message = "es campo mandatorio")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "issystemadministrator", nullable = false)
public boolean isSystemAdministrator() {
return isSystemAdministrator;
}
public void setSystemAdministrator(boolean isSystemAdministrator) {
this.isSystemAdministrator = isSystemAdministrator;
}
}
I am trying to filter just the first result of a query using Hibernate Criteria. Like this
public boolean existsSystemAdministrator() throws NoSuchAlgorithmException{
Criteria criteria=currentSession()
.createCriteria(User.class)
.add(Restrictions.eq("isSystemAdministrator", true));
return criteria.uniqueResult() != null;
}
But I always get org.hibernate.QueryException: could not resolve property: isSystemAdministrator exception
I have changed to all lowercase since the database field is like that, but it didn't work either. From what I've read Hibernate maps with the Java property, which hasn't been the case as well.
Have tried also change the isSystemAdministrator field to Boolean instead of boolean, but it didn't work out either.
I know this must sound stupid to any Hibernate guru, if someone can come up with an answer that would save me lots of time.
Thanks in advance.
You should adhere to the JavaBeans spec (http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html). The field should be 'systemAdministrator', and the method should be 'isSystemAdministrator'.
The problem is in #Id annotation in CatalogBase class. If you change so it will work fine:
#MappedSuperclass
public class CatalogBase {
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Id
#Type(type = "pg-uuid")
protected UUID id;
}
You can have 2 access types in Hibernate. Property access (as you did) or field access. Hibernate will guess the access type from the position of #Id or #EmbeddedId.
As I know (I am not a Hibernate guru), it should be no difference between these two access types. But some frameworks requires to have field access. Anyway, I do not know why your implementation does not work for querying and have not found any other explanation.

org.hibernate.MappingException: Could not determine t ype for: com.yammer.dropwizard.tailor.model.CustomerModel

I'm new to hibernate and web services and creating a project for tailor system in dropwizard.
When i try to run the project through cmd as in DropWizard Sites gets:
INFO [2014-01-18 08:41:13,784] org.hibernate.annotations.common.Version: HCANN0
00001: Hibernate Commons Annotations {4.0.1.Final}
INFO [2014-01-18 08:41:13,828] org.hibernate.Version: HHH000412: Hibernate Core
{4.1.9.Final}
INFO [2014-01-18 08:41:13,847] org.hibernate.cfg.Environment: HHH000206: hibern
ate.properties not found
INFO [2014-01-18 08:41:13,850] org.hibernate.cfg.Environment: HHH000021: Byteco
de provider name : javassist
INFO [2014-01-18 08:41:14,076] com.yammer.dropwizard.hibernate.SessionFactoryFa
ctory: Entity classes: [com.yammer.dropwizard.tailor.model.CoatModel, com.yammer
.dropwizard.tailor.model.CustomerModel, com.yammer.dropwizard.tailor.model.LongS
hirtModel, com.yammer.dropwizard.tailor.model.OrderModel, com.yammer.dropwizard.
tailor.model.ShirtModel, com.yammer.dropwizard.tailor.model.TailorModel, com.yam
mer.dropwizard.tailor.model.TrouserModel]
Exception in thread "main" org.hibernate.MappingException: Could not determine t
ype for: com.yammer.dropwizard.tailor.model.CustomerModel, at table: Order, for
columns: [org.hibernate.mapping.Column(customer)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:314)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:292)
at org.hibernate.mapping.Property.isValid(Property.java:239)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:4
69)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1294)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.jav
a:1742)
at com.yammer.dropwizard.hibernate.SessionFactoryFactory.buildSessionFac
tory(SessionFactoryFactory.java:77)
at com.yammer.dropwizard.hibernate.SessionFactoryFactory.build(SessionFa
ctoryFactory.java:35)
at com.yammer.dropwizard.hibernate.HibernateBundle.run(HibernateBundle.j
ava:38)
at com.yammer.dropwizard.hibernate.HibernateBundle.run(HibernateBundle.j
ava:13)
at com.yammer.dropwizard.config.Bootstrap.runWithBundles(Bootstrap.java:
64)
at com.yammer.dropwizard.cli.EnvironmentCommand.run(EnvironmentCommand.j
ava:37)
at com.yammer.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.jav
a:58)
at com.yammer.dropwizard.cli.Cli.run(Cli.java:53)
at com.yammer.dropwizard.Service.run(Service.java:61)
at com.yammer.dropwizard.tailor.service.TailorService.main(TailorService
.java:25)
Classes:
CustomerModel class:
#NamedQueries({
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.CustomerModel.findAll",
query = "SELECT c FROM CustomerModel c"
),
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.CustomerModel.findById",
query = "SELECT c FROM CustomerModel c WHERE c.ID = :ID"
)
})
#Entity
#Table(name = "Customer")
public class CustomerModel {
#Id
#GeneratedValue
#Column(name = "c_id")
int ID;
#Column(name = "c_code")
String customerCode;
#Column(name = "c_fname")
String firstName;
#Column(name = "c_mname")
String middleName;
#Column(name = "c_lname")
String lastName;
#Column(name = "c_nic")
String NIC_Number;
#Column(name = "c_email")
String email;
#Column(name = "c_pnumber")
String number;
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
}
public String getCustomerCode() {
return customerCode;
}
public void setCustomerCode(String customerCode) {
this.customerCode = customerCode;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getNIC_Number() {
return NIC_Number;
}
public void setNIC_Number(String NIC_Number) {
this.NIC_Number = NIC_Number;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}}
which other class should i list??
please help me.
More class:
Database Configuration class:
public class databaseConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
DatabaseConfiguration dbconfigurations = new DatabaseConfiguration();
public DatabaseConfiguration getDatabaseConfiguration() {
return dbconfigurations;
}
}
.YML file
dbconfigurations:
# the name of your JDBC driver
driverClass: org.sqlite.JDBC
# the username
user:
# the password
password:
url: jdbc:sqlite:TailorDB.db
Service Class:
public class TailorService extends Service<databaseConfiguration> {
public static void main(String[] args) throws Exception {
new TailorService().run(args);
}
private final HibernateBundle<databaseConfiguration> hibernate = new HibernateBundle<databaseConfiguration>(CustomerModel.class,OrderModel.class,CoatModel.class,LongShirtModel.class,ShirtModel.class,TailorModel.class,TrouserModel.class) {
#Override
public DatabaseConfiguration getDatabaseConfiguration(databaseConfiguration configuration) {
return configuration.getDatabaseConfiguration();
}
};
#Override
public void initialize(Bootstrap<databaseConfiguration> bootstrap) {
// TODO Auto-generated method stub
bootstrap.setName("tailor");
bootstrap.addBundle(hibernate);
}
#Override
public void run(databaseConfiguration configuration, Environment environment)
throws Exception {
// TODO Auto-generated method stub
final CustomerDAO cdao = new CustomerDAO(hibernate.getSessionFactory());
final OrderDAO odao = new OrderDAO(hibernate.getSessionFactory());
environment.addResource(new TailorResource(cdao,odao));
}
}
After the first glance it seems that your sessionFactory don't know about the CustomerModel entity. Make sure it is added into the sessionFactory as a mapping file.
From the other proposed answer: "Make sure it is added into the sessionFactory as a mapping file."
This is probably exactly what Dropwizard is trying to avoid. However, they did a really bad job at their tutorial page here http://dropwizard.codahale.com/manual/hibernate/
Basically, follow that page, this error is guaranteed what you will get. Because it did not cover a very important part. Someone was too careless or too lazy to copy paste the "Person" class they use in the tutorial. Here is it from https://github.com/dropwizard/dropwizard/blob/master/dropwizard-example/src/main/java/com/example/helloworld/core/Person.java
package com.example.helloworld.core;
import javax.persistence.*;
#Entity
#Table(name = "people")
#NamedQueries({
#NamedQuery(
name = "com.example.helloworld.core.Person.findAll",
query = "SELECT p FROM Person p"
),
#NamedQuery(
name = "com.example.helloworld.core.Person.findById",
query = "SELECT p FROM Person p WHERE p.id = :id"
)
})
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "fullName", nullable = false)
private String fullName;
#Column(name = "jobTitle", nullable = false)
private String jobTitle;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getJobTitle() {
return jobTitle;
}
public void setJobTitle(String jobTitle) {
this.jobTitle = jobTitle;
}
}
Now you can see that at the top, there is the annotation specify that this class is an entity and the table name. I spent 2 hours try to make sense of this problem. What were they thinking leaving this out of the tutorial!

#Column or #Basic JPA annotations ignored in spring-data-jpa schema creation

I am utterly confused by something I expected to work just out of the box. So either I am doing something totally wrong or this is just a misunderstanding.
I am trying to have a getter/setter annotation in a JPA Entity class. I sticked to an example I found on the JPA wiki (s. http://en.wikibooks.org/wiki/Java_Persistence/Basic_Attributes#Conversion). The example looks as follows:
#Entity
public class Employee {
...
private boolean isActive;
...
#Transient
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
#Basic
private String getIsActiveValue() {
if (isActive) {
return "T";
} else {
return "F";
}
}
private void setIsActiveValue(String isActive) {
this.isActive = "T".equals(isActive);
}
}
I took the clearest and cleanest spring-data-jpa example I could find: http://spring.io/guides/gs/accessing-data-jpa/.
I checked it out from git and changed their example entity class (s. https://github.com/spring-guides/gs-accessing-data-jpa/blob/master/complete/src/main/java/hello/Customer.java) to look as follows:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
#Transient
private boolean isActive;
#Transient
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
#Column
private String getIsActiveValue() {
if (isActive) {
return "T";
} else {
return "F";
}
}
private void setIsActiveValue(String isActive) {
this.isActive = "T".equals(isActive);
}
}
Now nothing changes. The respective String-Field does not get created. The line from the log creating the database table still looks as follows:
17:11:10.540 [main] DEBUG o.h.tool.hbm2ddl.SchemaUpdate - create table Customer (id bigint generated by default as identity, firstName varchar(255), lastName varchar(255), primary key (id))
I have absolutely no idea on what could be the reason for this. I could find not documentation that spring-data-jpa would not allow for annotations on getters.
Any help would be very, very appreciated!
I think you simply mixed the annotations: you must either annotate the fields, or the getters, but not both. Once you decided to annotate your ID field, you must annotate all the fields (and not getters), and the opposite: if you annotated your getId() method, that you must annotate all methods.
If you want it to work with the '#Transient' annotation you should do as Andrei suggests, you could add an extra field for isActiveValue but it is most important to annotate consistently otherwise you will get unpredictable behavior.
When annotating fields versus properties (getters and setters) it will make a difference.
In your case it looks like you want to do some logic in the getter hence annotating a field will likely not have the desired result. I don't particularly like the logic but understand that there is a need to annotate a getter.
Considering the logic in your code above I would simply eliminate the transient on the field altogether and put the logic with the annotations in the getters and setters.
#Entity
#Table(name = "Customer")
public class Customer {
private static final String IS_ACTIVE = "T";
private long id;
private String firstName;
private String lastName;
private String isActive = "";
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Column(unique = true, nullable = false)
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column(unique = true, nullable = false)
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(unique = true, nullable = false)
public String getIsActive() {
return isActive;
}
public void setIsActive(String isActive) {
this.isActive = isActive;
}
#Transient
public boolean isActive() {
return isActive.equals(IS_ACTIVE);
}
#Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}

Categories

Resources