I'm trying to generate my persistence layer with hibernate 4.3 and mysql 5.6 on netbeans 8.1, but each time it creates duplicated fields.
here is an example of what I'm getting:
private Set<Cliente> clientes = new HashSet<Cliente>(0);
private Set<Cliente> clientes_1 = new HashSet<Cliente>(0);
private Set<Compra> compras = new HashSet<Compra>(0);
private Set<Compra> compras_1 = new HashSet<Compra>(0);
private Set<Cotizacion> cotizacions = new HashSet<Cotizacion>(0);
private Set<Cotizacion> cotizacions_1 = new HashSet<Cotizacion>(0);
private Set<Credito> creditos = new HashSet<Credito>(0);
private Set<Credito> creditos_1 = new HashSet<Credito>(0);
private Set<Cuenta> cuentas = new HashSet<Cuenta>(0);
private Set<Cuenta> cuentas_1 = new HashSet<Cuenta>(0);
This problem is on each entity generated by hibernate. I also tried with the latest hibernate version which is 5.2 and the same problem happens.
I found out the problem, if you update from old version of hibernate to new version (4.3 to 5.2) when the property hibernate.hbm2ddl.auto* is set to **update, it forces hibernate to recreate the constraints, leaving the old ones in the db, when that happens the next time you try to recreate the persistence layer hibernate does not understands the old constraints as a relationship and so it creates the new properties.
Related
Disclaimer: It's hardly the first time I used JPA and Spring together, I've never observed this behaviour before.
The gist of the issue is as follows: The Spring managed JPA layer is apparently ignoring the vast majority of field/property annotations when mapping entities.
I'm using exclusively the Spring data jpa stuff here, from the spring boot starter 2.7.7.
Do note that I'm aware of the various Physical/implicit strategy things. They are PART of the issue but not THE issue.
Short example to explain:
#Entity // This works! Remove it and it's not detected
#Table(name = "PERSON") // This also works, remove it and the generated SQL goes against PERSONENTITY
public class PersonEntity extends IdEntity {
private static final long serialVersionUID = 1L;
// #Column(name = "NAME") <- It's commented but it's still in the generated SQL queries
private String name;
#Column(name = "CAMEL_CASED_FIELD") // In this case the PHYSICAL naming strategy should apply, however it's ignored and it falls back to the implicit one, meaning it's ignoring this annotation.
private String camelCasedField;
#Column(name = "GENERATED", insertable=false, updatable=false) // Gets ignored, insert/update statements include the field
private String generated;
#org.springframework.data.annotation.Transient // Gets ignored, insert/update statements include the field
private String field1;
#javax.persistence.Transient // Gets ignored, insert/update statements include the field
private String field2;
#ReadOnlyProperty // Gets ignored, insert/update statements include the field
private String field3;
#Immutable // Gets ignored, insert/update statements include the field
private String field4;
// <cut for brevity, just getters and setters>
Now, the column naming issue I can live with, however I NEED at the very least the "insertable/updatable" statements to be correctly taken into account as it's a database generate column and inserts WILL generate errors and rollbacks.
The configuration is ALL Spring based, no persistence.xml (which I've also tried, but to no avail).
The relevant bit:
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory (
DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl
) {
Map<String, Object> jpaPropertiesMap = new java.util.HashMap<>(jpaProperties.getProperties());
jpaPropertiesMap.put(Environment.PHYSICAL_NAMING_STRATEGY, PhysicalNamingStrategyStandardImpl.class.getName());
// Uncommenting this line will map column names correctly but only by accident
// Besides,with the #Column(name = "XX") annotation it should use the PHYSICAL strategy.
// jpaPropertiesMap.put(Environment.IMPLICIT_NAMING_STRATEGY, ImplicitNamingStrategyJpaCompliantImpl.class.getName());
jpaPropertiesMap.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
jpaPropertiesMap.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
jpaPropertiesMap.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
jpaPropertiesMap.put("hibernate.integrator_provider", MetadataExtractorIntegrator.PROVIDER);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.foo.bar.api.model");
em.setPersistenceUnitName("my-unit");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaPropertyMap(jpaPropertiesMap);
return em;
}
I have tried literally any combination of configurations I can think of (like using the Persistence Provider instead of using the Adapter) but the results always fall exactly in two camps:
The configuration just isn't valid and the application doesn't start at all
The configuration is valid but gives me the exact same results as stated above.
I tried to see if there's some conflicting items in the pom.xml but there's only spring data that's relevant.
Additionally, this configuration was taken almost verbatim from another project (different versions and far more complex but still Spring Boot) and there it worked as expected.
I'm at wits' end.
Assuming I have a combo box in Vaadin CrudEditor
so this will be the code of the combo box part:
ComboBox<Driver> driversComboBox = new ComboBox<>("Drivers");
ComboBox.ItemFilter<Driver> filter = (driver, filterString) ->
driver.getFullName().toLowerCase().contains(filterString.toLowerCase());
driversComboBox.setItems(filter, driverService.findAll());
driversComboBox.setItemLabelGenerator(Driver::getFullName);
and this is the binder:
binder.forField(driversComboBox).asRequired().bind(Transporter::getDrivers, Transporter::setDrivers);
but this binder is wrong, I get this error:
Bad return type in method reference: cannot convert java.util.Set<org.vaadin.webinar.security.sampleapp.Entity.Driver> to org.vaadin.webinar.security.sampleapp.Entity.Driver
So, Transporter model:
public class Transporter extends AbstractEntity{
...
#OneToMany(mappedBy = "transporter", fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, targetEntity = Driver.class)
private Set<Driver> drivers = new HashSet<>();
}
So, in short, How to bind the combobox with a List?
Thanks
There is no Vaadin component but there is a 3rd party component https://vaadin.com/directory/component/multiselect-combo-box
But be aware that this component currently doesn't work with Vaadin 22. It works fine until Vaadin 21.
I have a Spring Boot project with multiple databases of different years and these databases have same tables so the only difference is the year (..., DB2016, DB2017). In the controller of the application i need to return data that belong to "different" years. Moreover in future years other databases will be created (eg. in 2018 there's going to be a db named "DB2018"). So my problem is how to switch the connection among databases without creating a new datasource and a new repository every new year.
In an other question posted by me (Spring Boot - Same repository and same entity for different databases) the answer was to create different datasources and different repositories for every existing database, but in this case i want to return data from existing databases on the basis of the current year. More specifically:
SomeEntity.java
#Entity(name = "SOMETABLE")
public class SomeEntity implements Serializable {
#Id
#Column(name="ID", nullable=false)
private Integer id;
#Column(name="NAME")
private String name;
}
SomeRepository.java
public interface SomeRepository extends PagingAndSortingRepository<SomeEntity, Integer> {
#Query(nativeQuery= true, value = "SELECT * FROM SOMETABLE WHERE NAME = ?1")
List<SomeEntity> findByName(String name);
}
SomeController.java
#RequestMapping(value="/foo/{name}", method=RequestMethod.GET)
public ResponseEntity<List<SomeEntity>> findByName(#PathVariable("name") String name) {
List<SomeEntity> list = autowiredRepo.findByName(name);
return new ResponseEntity<List<SomeEntity>>(list,HttpStatus.OK);
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/DB
spring.datasource.username=xxx
spring.datasource.password=xxx
So if the current year is 2017 i want something like this:
int currentyear = Calendar.getInstance().get(Calendar.YEAR);
int oldestDbYear = 2014;
List<SomeEntity> listToReturn = new LinkedList<SomeEntity>();
//the method getProperties is a custom method to get properties from a file
String url = getProperties("application.properties", "spring.datasource.url");
props.setProperty("user", getProperties("application.properties","spring.datasource.username"));
props.setProperty("password", getProperties("application.properties","spring.datasource.password"));
for (int i = currentYear, i>oldestDbYear, i--) {
//this is the connection that must be used by autowiredRepo Repository, but i don't know how to do this.
//So the repository uses different connection for every year.
Connection conn = getConnection(url+year,props);
List<SomeEntity> list_of_specific_year = autowiredRepo.findByName(name);
conn.close;
listToReturn.addAll(list_of_specific_year);
}
return listToReturn;
Hope everithing is clear
The thing that is probably most suitable to your needs here is Spring's AbstractRoutingDataSource. You do need to define multiple DataSources but you will only need a single repository. Multiple data sources is not an issue here as there is always a way to create the DataSource beans programatically at run time and register them with the application context.
How it works is you basically register a Map<Object, DataSource> inside your #Configuration class when creating your AbstractRoutingDataSource #Bean and in this case the lookup key would be the year.
Then you need create a class that implements AbstractRoutingDataSource and implement the determineCurrentLookupKey() method. Anytime a database call is made, this method is called in the current context to lookup which DataSource should be returned. In your case it sounds like you simply want to have the year as a #PathVariable in the URL and then as the implementation of determineCurrentLookupKey() grab that #PathVariable out of the URL (e.g in your controller you have mappings like #GetMapping("/{year}/foo/bar/baz")).
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
.getRequestAttributes()).getRequest();
HashMap templateVariables =
(HashMap)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return templateVariables.get("year");
I used this approach when writing a testing tool for a product where there were many instances running on multiple different servers and I wanted a unified programming model from my #Controllers but still wanted it to be hitting the right database for the server/deployment combination in the url. Worked like a charm.
The drawback if you are using Hibernate is that all connections will go through a single SessionFactory which will mean you can't take advantage of Hibernate's 2nd level caching as I understand it, but I guess that depends on your needs.
I have a one-to-many relationship defined as below
#Cacheable
#Entity
#NamedEntityGraph(
name = "Parent.Child",
attributeNodes = {
#NamedAttributeNode("children"),
}
)
public class Parent {
private Set<Child> children;
// getter - setter
}
Now in my DAL, i'm calling this method
#Override
public Parent getParentWithChildren(int id) {
EntityGraph<?> graph = entityManager.getEntityGraph("Parent.Child");
Map<String, Object> props = new HashMap<>();
props.put("javax.persistence.fetchgraph", graph);
return entityManager.find(Parent.class, id, props);
}
Since i have loaded Parent with Children, i should be able to use children collection outside of the transaction. But i'm getting Lazyinitialization Exception. This happens only when hibernate level 2 cache - ehcache is enabled. If i disable it from config, it works as expected. Also if i initialize collection explicitly after find, it works as expected. So is it a bug?. I'm using Hibernate 5.2.6.Final with JPA 2.1.
EDIT: One more thing i noticed was that entity loads fine for the first time, so that problem must be related with hibernate & cache provider.
In order for Hibernate to use an entity graph one must bypass the second-level cache (e.g. EhCache) by using the javax.persistence.cache.retrieveMode hint with the value CacheRetrieveMode.BYPASS:
final EntityGraph<?> graph = em.getEntityGraph("myGraph");
final Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
hints.put("javax.persistence.fetchgraph", graph);
final SomeEntity entity = em.find(SomeEntity.class, 42, hints);
Note that the second-level cache will still be populated as usual.
No. It's not a bug. I guess Hibernate is delaying the loading from cache until really necessary. The implementation can decide to be lazy as see fit unless you ask for eager fetching.
So the general rule is to load everything you need before going out of the transaction (before closing the Hibernate session to be precise).
I need to write some temporary code in my existing Spring Boot 1.2.5 application that will do some complex SQL queries. By complex, I mean a single queries about 4 different tables and I have a number of these. We all decided to use existing SQL to reduce potential risk of getting the new queries wrong, which in this case is a good way to go.
My application uses JPA / Hibernate and maps some entities to tables. From my research it seems like I would have to do a lot of entity mapping.
I tried writing a class that would just get the Hibernate session object and execute a native query but when it tried to configure the session factory it threw an exception complaining it could not find the config file.
Could I perhaps do this from one of my existing entities, or at least find a way to get the Hibernate session that already exists?
UPDATE:
Here is the exception, which makes perfect sense since there is no config file to find. Its app configured in the properties file.
org.hibernate.HibernateException: /hibernate.cfg.xml not found
at org.hibernate.internal.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:173)
For what it's worth, the code:
#NamedNativeQuery(name = "verifyEa", query = "select account_nm from per_person where account_nm = :accountName")
public class VerifyEaResult
{
private SessionFactory sessionFact = null;
String accountName;
private void initSessionFactory()
{
Configuration config = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).getBootstrapServiceRegistry();
sessionFact = config.buildSessionFactory(serviceRegistry);
}
public String getAccountName()
{
// Quick simple test query
String sql = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = 'lynnedelete'";
initSessionFactory();
Session session = sessionFact.getCurrentSession();
SQLQuery q = session.createSQLQuery(sql);
List<Object> result = q.list();
return accountName;
}
}
You can use Data access with JDBC, for example:
public class Client {
private final JdbcTemplate jdbcTemplate;
// Quick simple test query
final static String SQL = "SELECT * FROM PER_ACCOUNT WHERE ACCOUNT_NM = ?";
#Autowired
public Client(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getData(String name) {
return jdbcTemplate.queryForList(SQL, name);
}
}
The short way is:
jdbcTemplate.queryForList("SELECT 1", Collections.emptyMap());