JPA/Hibernate doing update in table without any command - java

Well, I work with JSF + Hibernate/JPA, I have the following code:
public void loadAddressByZipCode(ActionEvent event){
Address address = boDefault.findByNamedQuery(Address.FindByZipCode, 'zipCode', bean.getAddress().getZipCode());
if (address == null){
//This zip code not exists in db
}else{
bean.setAddress(address);
}
}
This method above is called in every "onBlur" in inputText component in XHTML page, this inputText have the property value like this: "#{addressMB.bean.address.zipCode}"
So, when user (in XHTML page) type a new Zip Code this value is setted in "bean.getAddress().setZipCode()" and i search for this value in Database. But debugging my application i discovered that when i do "bean.getAddress().getZipCode()" the Hibernate launch a "update address set ..." in my DataBase. How can i prevent this and why this happens ?
EDIT 1:
This is my real method implemented
public void carregarLogradouroByCep(AjaxBehaviorEvent event) {
List listReturn = getBoPadrao().findByNamedQuery(
Logradouro.FIND_BY_CEP,
new NamedParams("cep", bean.getLogradouro().getCep()));
if (listReturn.size() > 0) {
Logradouro logradouro = (Logradouro) listReturn.get(0);
bean.setLogradouro(logradouro);
}else{
bean.setLogradouro(new Logradouro());
}
}
This is my component with onblur event:
<p:inputText value="#{enderecoMB.bean.logradouro.cep}" id="cep"
required="true" requiredMessage="O CEP é obrigatório">
<p:ajax event="blur"
listener="#{enderecoMB.carregarLogradouroByCep}"
update=":formManterEndereco:logradouro, :formManterEndereco:cidade, :formManterEndereco:estado,
:formManterEndereco:bairro" />
</p:inputText>
I have a "BasicDAOImpl" that make all operations in database, so tried do it in my entityManager:
#PostConstruct
private void init(){
entityManager.setFlushMode(FlushModeType.COMMIT);
}
But "update automatically" continue.
EDIT 2: My FIND_BY_CEP query
"SELECT c FROM Endereco c JOIN FETCH c.tipoEndereco JOIN FETCH c.logradouro"
EDit 3: My Entities
Endereco.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Endereco.findAllCompleto", query = "SELECT c FROM Endereco c "
+ "JOIN FETCH c.tipoEndereco " + "JOIN FETCH c.logradouro") })
#Table(name = "endereco")
public class Endereco extends AbstractBean {
/**
*
*/
private static final long serialVersionUID = 5239354646908722819L;
public Endereco(){
logradouro = new Logradouro();
}
#Transient
public static final String FIND_ALL_COMPLETO = "Endereco.findAllCompleto";
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_tipo_endereco")
private TipoEndereco tipoEndereco;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_logradouro")
private Logradouro logradouro;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_pessoa")
private Pessoa pessoa;
#Column
private String numero;
#Column
private String complemento;
public Logradouro getLogradouro() {
return logradouro;
}
public void setLogradouro(Logradouro logradouro) {
this.logradouro = logradouro;
}
public TipoEndereco getTipoEndereco() {
return tipoEndereco;
}
public void setTipoEndereco(TipoEndereco tipoEndereco) {
this.tipoEndereco = tipoEndereco;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
public Pessoa getPessoa() {
return pessoa;
}
public void setPessoa(Pessoa pessoa) {
this.pessoa = pessoa;
}
}
Logradouro.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Logradouro.findByCep", query = "SELECT c FROM Logradouro c "
+ "JOIN FETCH c.bairro "
+ "JOIN FETCH c.cidade "
+ "WHERE c.cep = :cep "
+ "ORDER BY c.logradouro") })
#Table(name = "logradouro")
public class Logradouro extends AbstractBean {
public Logradouro(){
this.cidade = new Cidade();
this.bairro = new Bairro();
}
/**
*
*/
private static final long serialVersionUID = 1L;
#Transient
public static final String FIND_BY_CEP = "Logradouro.findByCep";
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_cidade")
private Cidade cidade;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_bairro")
private Bairro bairro;
#Column
private String cep;
#Column
private String logradouro;
public Cidade getCidade() {
return cidade;
}
public void setCidade(Cidade cidade) {
this.cidade = cidade;
}
public Bairro getBairro() {
return bairro;
}
public void setBairro(Bairro bairro) {
this.bairro = bairro;
}
public String getCep() {
return cep;
}
public void setCep(String cep) {
this.cep = cep;
}
public String getLogradouro() {
return logradouro;
}
public void setLogradouro(String logradouro) {
this.logradouro = logradouro;
}
}

Hibernate uses an entitymanager to do automatic handling of your entities by default. Your bean is one such entity so whenever the entitymanager feels it's time to flush to the database it does so. If you want to flush manually, you'll want to have a look at such a construction:
entityManager.setFlushMode(javax.persistence.FlushModeType.MANUAL);

I think that you misuse Hibernate. The main idea of ORM that is allows you to write code, ignoring that fact, that something goes into database. Your domain model must be completetly testable without any database. ORM is not abstraction to manage with relation database, it is abstraction for your object model. If you need to explicitly do inserts and updates, you better use some active record implementation or look at lightweight mapper like MyBatis.
And why you want to prevent database update? Property is changed, right? So let Hibernate decide, whether and when its new value must be written to database.

Related

How to bind a foreign key in Vaadin 8

I cannot resolve a problem. I have a window with TextField and I want to bind a foreign key but have no ideas what am I doing wrong. I've already read the answer here: Binding foreign key in Vaadin (EclipseLink)
I decided to use Converter, but still stumble.
So, there are two entities (simplified): Client and Order. An existent or a new client can have several orders, means that Order.clientID is foreign key
Client entity:
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name = "Clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ClientID", updatable = false, nullable = false)
private Long clientID;
#Column(name = "Name", nullable = false, length = 20)
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "clientID")
private Set<Order> orders;
public Long getId() { return clientID; }
public void setId(Long clientID) { this.clientID = clientID; }
public String getName() { return firstName; }
public void setName(String name) { this.name = name; }
}
Order entity:
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name = "Orders")
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "OrderID", nullable = false)
private Long orderID;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ClientID",
referencedColumnName = "ClientID",
updatable = false,
nullable = false)
private Client clientID;
#Column(name = "Description", nullable = false, length = 1000)
private String description;
public Long getOrderID() { return orderID; }
//public Long getClientID() { return clientID.getId(); }
public Client getClientID() { return clientID; }
public void setClientID(Client clientID) { this.clientID = clientID; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
and I want to bind Order.clientID to the TextField. But IDEA highlight the setter setClientID as "Cannot resolve method 'setClientID'"
public class AddOrderModalView extends Window {
private OrderService orderService = new OrderService();
private Order order = new Order();
Binder<Order> binder = new Binder<>(Order.class);
private ChangeHandler changeHandler = new ChangeHandler() {
#Override
public void onChange() {
}
};
private FormLayout formLayout = new FormLayout();
private TextField clientId = new TextField("Client ID");
private TextField description = new TextField("Description");
private Button save = new Button("Save");
private Button cancel = new Button("Cancel");
public AddOrderModalView() {
super("Add a new order");
VerticalLayout subContent = new VerticalLayout();
subContent.setSizeFull();
HorizontalLayout actions = new HorizontalLayout();
actions.addComponents(save, cancel);
formLayout.addComponents(clientId, description);
subContent.addComponent(formLayout);
setContent(subContent);
save.addStyleNames(ValoTheme.BUTTON_SMALL, ValoTheme.BUTTON_PRIMARY);
cancel.addStyleName(ValoTheme.BUTTON_SMALL);
save.addClickListener(e -> save());
cancel.addClickListener(e -> close());
bindingFields();
setModal(true);
}
private void bindingFields() {
binder.forField(clientId)
.withConverter(Long::valueOf, String::valueOf)
.bind(Order::getClientID, Order::setClientID); //the error is here
binder.forField(this.description)
.withValidator(new StringLengthValidator(
"Please add description. The maximum length is 1000 characters", 1, 1000))
.bind(Order::getDescription, Order::setDescription);
binder.bindInstanceFields(this);
binder.setBean(order);
}
public interface ChangeHandler {
void onChange();
}
private void save() {
if (binder.validate().isOk()) {
orderService.persist(order);
close();
changeHandler.onChange();
}
}
}
ClientToClientIdConverter:
public class ClientToClientIdConverter implements Converter<String, Client> {
#Override
public Result<Client> convertToModel(String s, ValueContext valueContext) {
return Result.error("not supported");
}
#Override
public String convertToPresentation(Client client, ValueContext valueContext) {
return Objects.toString(client.getId(), "");
}
}
Could anyone help me with solving the problem?
Answer to the question: How to bind foreign key (or any other nested property) in a textfield (not what you need!)
You can do it by providing lambda expressions to get and set the nested properties.
TextField clientId = new TextField("Client ID");
binder.forField(clientId)
.withConverter(new StringToLongConverter("error message"))
.bind(item -> item.getClient().getId(), (item, value) -> item.getClient().setId(value));
This code can be cause of NullPointerExceptions if the order can have no client at this point. If that is possible, then use this instead (added checking for nullvalues):
TextField clientId = new TextField("Client ID");
binder.forField(clientId)
.withConverter(new StringToLongConverter("error message"))
.bind(
item -> item.getClient() != null ? item.getClient.getId() : null,
(item, value) -> {
if(item.getClient() != null){
item.getClient().setId(value);
}
});
Warning! Please know that manually changing the value in this textfield will change the id of it's already assigned client, and not select/assign a new Client for this Order. If it's the latter that you want, use a ComboBox instead! I'm not sure if it ever makes sense to do the first, but I answered because you asked. I am now sure that you want the latter option, so please follow the next part.
The actual solution to your problem: It seems that you indeed need a ComboBox, because you want to select/assign a client for the order.
So what you need is basically this:
ComboBox<Client> clientSelection = new ComboBox<Client>("client");
clientSelection.setItems(clientService.findAll()); // list/set of possible clients.
// Using clients name for item captions now, but you can also use id or both
clientSelection.setItemCaptionGenerator(Client::getName);
binder.forField(clientSelection)
.bind(Order::getClient, Order::setClient);
This way, you can select a client which then gets set as the bound orders client.
Your binding doesnt work because your setClient method expects an Object of type Client as parameter not an Long. You should change that to:
public void setClientID(Long clientID) { this.clientID = clientID; }
You could also use an ComboBox for the Selection of Customer, there is an example how to bind Objects to a ComboBox on the Vaadin Website.

Hibernate call DELETE from table after method end

I have problem, and I don't know how to solve it.
I have entity:
#Entity
#Table(name = "entity_languagetree")
#AttributeOverride(name = "id", column = #Column(name = "languagetree_id"))
public class LanguageTree extends BaseObject {
#ElementCollection(targetClass = java.lang.String.class, fetch = FetchType.EAGER)
#CollectionTable(name = "view_languagetree_to_stringlist")
private List<String> relationship = new ArrayList<>();
public LanguageTree() {
//
}
public List<String> getRelationship() {
return relationship;
}
public void setRelationship(List<String> relationship) {
this.relationship = relationship;
}
}
where BaseObject is
#MappedSuperclass
public class BaseObject {
#Id
#GeneratedValue
#Column(name = "entity_id")
private Long id;
/**
*
* #return true if the entity hasn't been persisted yet
*/
#Transient
public boolean isNew() {
return id == null;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bean getBean() {
return null;
}
}
Work with object - in my servlet, I am calling jsVarTree() like this:
String var = jsVarTree();
My problem is, that after method jsVarTree is finished, hibernate delete my relationship list from entity LanguageTree. I don't know why! I am not calling any delete and etc.. (I AM SURE, I SPENT A LOT OF TIME IN DEBUGER!)
:
#Override
public String jsVarTree() {
TreeBuilder tb = new TreeBuilder(getLanguageList());
return tb.getJsVarString(); // THIS METHOD IS ONLY GETTER !!!!
}
#Override
public List<String> getLanguageList() {
LanguageTree lt = getLanguageTreeObject();
return lt.getRelationship();
}
#Override
public LanguageTree getLanguageTreeObject() {
long fakingId = languageTreeDao.getLastId();
ServerLogger.logDebug("LAST FAKING ID: " +fakingId);
return languageTreeDao.findOne(fakingId);
}
I found this log in loggor:
HibernateLog --> 15:01:03 DEBUG org.hibernate.SQL - delete from
view_languagetree_to_stringlist where LanguageTree_languagetree_id=?
Can somebody tell me, why hibernate call delete over my table?
I saw a table in phpmyadmin..
TABLE IS FULL.
String var = jsVarTree();
TABLE IS EMPTY.
Table is deleted after return tb.getJsVarString(); is finished.
Thank you for any help!

Can't remove entity with JPA

I'm trying to remove an entity from memory (at the moment I don't use DB) with JPA, when I use remove and then try to find the deleted entity it shows null, but when I use findAll method it retrieve all data (with removed entity)...
Profile.java
#Entity
#Table(name = "profile")
public class Profile {
#Id
#GeneratedValue
private Long id;
private String nombre;
private Boolean restrictedAccess;
private Boolean canValidate;
// private Set<AccessField> accessFields = new HashSet<AccessField>();
// private Set<AccessEntity> accessEntities = new HashSet<AccessEntity>();
#OneToMany(mappedBy = "profile", fetch = FetchType.EAGER)
private Set<AccessMenu> menuSections = new HashSet<AccessMenu>();
#OneToMany(mappedBy = "profile", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
[getters and setters]
ProfileRepository
#Repository
#Transactional
public class ProfileRepository {
#PersistenceContext
private EntityManager entityManager;
public Profile save(Profile p) {
p = this.entityManager.merge(p);
this.entityManager.flush();
return p;
}
public void delete(Long id){
Profile profile = this.entityManager.find(Profile.class, id);
this.entityManager.remove(profile);
}
public List<Profile> findAll() {
CriteriaQuery cq = this.entityManager.getCriteriaBuilder().createQuery();
cq.select(cq.from(Profile.class));
return (List<Profile>) this.entityManager.createQuery(cq).getResultList();
}
public Profile findById(Long id){
return this.entityManager.find(Profile.class, id);
}
}
Controller method
#RequestMapping(value="profile/delete/{idProfile}", method = RequestMethod.GET)
public String delete(#PathVariable String idProfile,RedirectAttributes ra, Model model){
profileRepo.delete(Long.valueOf(idProfile));
model.addAttribute("profiles", profileRepo.findAll());
return "profile/list";
}
if you are are trying to delete an entity by using Id in the controller, do it like profileRepo.deleteById(Long.valueOf(idProfile));
this, not like this profileRepo.delete(profileRepo.findById(Long.valueOf(idProfile)));
Also use your repository functions like these,
public void deleteArtistById(Long artistId) {
Artist artist = manager.find(Artist.class, artistId);
if (artist != null) {
manager.getTransaction().begin();
manager.remove(artist);
manager.getTransaction().commit();
}
}
public void deleteArtist(Artist artist) {
manager.getTransaction().begin();
manager.remove(artist);
manager.getTransaction().commit();
}
You can take a look at this link for more detail:
http://kodejava.org/how-do-i-delete-entity-object-in-jpa/
At last I found a solution, the problem was when I tried to remove Profile, users and menuSections have related data, so finally I put in menuSections a cascade = CascadeType.REMOVE and in users set profile property to null

Dropwizard:"error processing your request" for retriving data from database

When i tries to open
http://localhost:8080/tailor/orders
which should return all the orders in database.but it's generating error
{"message":"There was an error processing your request. It has been logged (ID fe49a13e76c59894)."}
I'm unable to trace the problem what's causing this.
I'm using dropwizard for restful web service and hibernate from dropwizard for sqlight database.
Class:
Resources class:
#Path("/tailor")
#Produces(MediaType.APPLICATION_JSON)
public class TailorResource {
OrderDAO orderdao;
public TailorResource(OrderDAO odao) {
this.orderdao = odao;
}
#GET
#Path("/orders")
#Produces(MediaType.APPLICATION_JSON)
public List<OrderModel> getAllOrders() {
return orderdao.findAll();
}
#GET
#Path("/orders/{id}")
#Produces(MediaType.APPLICATION_JSON)
public OrderModel getOrderById(#PathParam("id") int id) {
return orderdao.findById(id);
}
}
OrderDAO class:
public class OrderDAO extends AbstractDAO<OrderModel>{
public OrderDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public OrderModel findById(int id) {
return get(id);
}
public OrderModel create(OrderModel o) {
return persist(o);
}
public List<OrderModel> findAll() {
return list(namedQuery("com.yammer.dropwizard.tailor.model.OrderModel.findAll"));
}}
Order Class:
#NamedQueries({
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.OrderModel.findAll",
query = "SELECT o FROM OrderModel o"
),
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.OrderModel.findById",
query = "SELECT o FROM OrderModel o WHERE o.ID = :ID"
)
})
#Entity
#Table(name = "Order")
public class OrderModel {
#Id
#GeneratedValue
#Column(name = "o_id")
int ID;
#Column(name = "o_shirt_quantity")
int shirtQuantity;
#Column(name = "o_longshirt_quantity")
int longshirtQuantity;
#Column(name = "o_trouser_quantity")
int trouserQuantity;
#Column(name = "o_coat_quantity")
int coatQuantity;
#Column(name = "o_deliverydate")
Date deliveryDate;
#Column(name = "o_orderdate")
Date orderDate;
#Column(name = "o_shirt_price")
Double shirtPrice;
#Column(name = "o_longshirt_price")
Double longshirtPrice;
#Column(name = "o_trouser_price")
Double trouserPrice;
#Column(name = "o_coat_price")
Double coatPrice;
#Column(name = "o_totalamount")
Double totalAmount;
#Column(name = "o_discount")
Double discount;
#Column(name = "o_advancedpayment")
Double advancedPayment;
#Column(name = "o_remainingpayment")
Double remainingPayment;
#Column(name = "o_orderstatus")
int orderStatus;
}
Database configuration class:
public class databaseConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
DataSourceFactory dbconfigurations = new DataSourceFactory();
public DataSourceFactory getDataSourceFactory() {
//return dbconfigurations;
Map<String,String> s=new HashMap<String,String>();
s.put("hibernate.dialect","Hibernate.SQLightDialect.SQLiteDialect");
dbconfigurations.setProperties(s);
return dbconfigurations;
}
}
Main service Class:
public class TailorApplication extends Application<databaseConfiguration> {
public static void main(String[] args) throws Exception {
new TailorApplication().run(args);
}
private final HibernateBundle<databaseConfiguration> hibernate = new HibernateBundle<databaseConfiguration>(CustomerModel.class) {
{
#Override
public DataSourceFactory getDataSourceFactory(databaseConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
#Override
public void initialize(Bootstrap<databaseConfiguration> bootstrap) {
// TODO Auto-generated method stub
bootstrap.addBundle(hibernate);
}
#Override
public void run(databaseConfiguration configuration, Environment environment)
throws Exception {
final OrderDAO odao = new OrderDAO(hibernate.getSessionFactory());
environment.jersey().register(new TailorResource(odao));
}
}
YML file:
dbconfigurations:
# the name of your JDBC driver
driverClass: org.sqlite.JDBC
# the username
user:
# the password
password:
url: jdbc:sqlite:TailorDB.db
Help please?
Make sure you filled the application class with the entities:
private final HibernateBundle<AppConfiguration> hibernateBundle = new HibernateBundle<AppConfiguration>(
//
//
//ADD ENTITIES HERE
//
//
Person.class
,Product.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
On the face of it, looks like you have missed the #UnitOfWork annotation on getAllOrders.
Having said that, the error you have shared is the external messaging that DW provides by default. Instead you should look at your web service logs for the exact error and precise stacktrace. If you run your service in terminal with java -jar path/to/shaded.jar server my.yml you should see elaborate error on the console. Please share that so that community can help better.

How to access Object.field in criteria in Hibernate

i have write the criteria for company class.
below are company class, companySearch class and criteria. But criteria list is throw exception. exception is "org.hibernate.QueryException: could not resolve property: san.san of: com.sesami.common.domain.Company". How to access Company.san.san?
Company class
public class Company extends DomainObject implements UserDetails {
private Long id;
private String companyName;
private CompanyType companyType;
private String description;
private String companyURL;
private String billToEmail;
private String hashPassword;
private SAN san;
#OneToOne(cascade = { CascadeType.ALL })
public SAN getSan() {
return san;
}
public void setSan(SAN san) {
this.san = san;
}
...
}
CompanySearch
public class CompanySearch {
private String companyName;
private String email;
private Long san;
private String gstNumber;
......
public Long getSan() {
return san;
}
public void setSan(Long san) {
this.san = san;
}
...
}
Criteria
companyCriteria = this.getSession().createCriteria(
Company.class);
if (companySearch.getSan() != null
&& !"".equals(companySearch.getSan()))
companyCriteria.add(Restrictions.eq("san.san",
companySearch.getSan()));
Integer count = ((Long) companyCriteria.setProjection(
Projections.rowCount()).uniqueResult()).intValue();
companyCriteria.setProjection(null);
companyCriteria.setResultTransformer(Criteria.ROOT_ENTITY);
companyCriteria
.setFirstResult((pager.getPage() - 1) * pager.getPageSize())
.setMaxResults(pager.getPageSize()).list();
List<Company> companies = companyCriteria.list();
PagedResultSet pr = new PagedResultSet();
pr.setPager(pager);
pr.setResultSet(companies);
pr.setRowCount(count);
return pr;
You must create a join to the San entity, using a subcriteria, or an alias:
companyCriteria.createAlias("san", "sanAlias");
companyCriteria.add(Restrictions.eq("sanAlias.san",
companySearch.getSan()));
or
companyCriteria.createCriteria("san").add(Restrictions.eq("san",
companySearch.getSan()));
This is well explained in the Hibernate reference documentation and even in the Criteria javadoc.
Note that this has absolutely nothing to do with Spring, and everything to do with Hibernate. If you searched in the Spring doc for how to do this, no wonder you didn't find anything.

Categories