How can I perform a JPA query for another object?
in the normal case we do :
public interface Object1Repository extends JpaRepository<Object1, Long> { public List<Object2> findById(Long id);
}
but here i want to do the samething with another object (object2) :
public interface Object1Repository extends JpaRepository<Object1, Long> {
public List<Object2> findById(Long id); }
I want to reuse my AbstractDAO in a new project, except this time I don't want to use EJB annotations - just CDI ones.
So far, I've been using it like this:
public abstract class AbstractDAO<T> {
#PersistenceContext(unitName = "myUnit")
private EntityManager entityManager;
private Class<T> entityClass;
public AbstractDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
public void save(T entity) {
entityManager.persist(entity);
}
public void update(T entity) {
entityManager.merge(entity);
}
public void remove(T entity) {
entityManager.remove(entityManager.merge(entity));
}
public T findById(Object id) {
return entityManager.find(entityClass, id);
}
public List<T> findBy(String attrName, Object attrValue) {
// Impl here
}
// [...] Many more search methods
}
And I've been creating a DAO for each Entity, like this for example:
#Stateless
public class UserDAO extends AbstractDAO<User> {
public UserDAO() {
super(User.class);
}
public User findByUsername(String username) {
if (username != null) {
return super.findOneBy("username", username.toLowerCase());
}
return null;
}
}
Now I would like to get rid of the #Stateless annotation. But simply replacing it with a #RequestScoped one won't work because of the non-private constructor with no parameters requirement of JSR-346
How can I refactor my DAO to a pure CDI one ?
Two issues here: CDI beans are not transaction aware by default -unlike EJBs, so you will have to use #Transactional qualifier if you want to do saves/updates...
Second, your no-arg constructor: you only need to pass the entity class to your abstract class even though you also specify it as a generic argument. You can infer the actual class like this:
public class AbstractDAO<T> {
private transient Class<T> entityClass;
#SuppressWarnings("unchecked")
public AbstractDAO() {
Type generSuperCls = getClass().getGenericSuperclass();
if (generSuperCls instanceof Class) {
generSuperCls = ((Class<?>) generSuperCls).getGenericSuperclass();
}
ParameterizedType parameterizedType = (ParameterizedType) generSuperCls;
Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof Class) {
this.entityClass = (Class<T>) type;
} else if (type instanceof ParameterizedType) {
this.entityClass = (Class<T>) ((ParameterizedType) type).getRawType();
}
}
#PersistenceContext
private EntityManager em;
public T getById(Object id) throws ServiceException {
return getEm().find(entityClass, id);
}
// other methods follow
}
As a side note, why do you want to get rid of EJBs? Benchmarks show you get better performance using pooled slsb than cdi and they together very well(every EJB beans is also a CDI bean in jee container).
I have a simple WebSocket set up and try to save data. Somehow the data gets not persisted. I don't get any error messages and the object gets returned correct to the client. If I try to store the object with a REST controller and a REST request it works.
Here are the dependencies of my build.gradle file:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'org.springframework.boot:spring-boot-starter-websocket'
compile 'org.springframework:spring-messaging'
compile 'com.google.code.gson:gson:1.7.2'
compile 'org.postgresql:postgresql:9.4-1200-jdbc41'
compile 'commons-dbcp:commons-dbcp:1.4'
testCompile('org.springframework.boot:spring-boot-startet-test')
}
PersonController
#Controller
public class PersonController {
#Autowired
PersonRepository personRepository;
#MessageMapping("/test")
#SendTo("/response/test")
public Person test() throws Exception {
Person person = new Person();
person.setName("John Doe");
return personRepository.save(person);
}
}
Configuration for STOMP messaging
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/response");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
Person entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return getName;
}
public void setName(String name) {
this.name = name;
}
}
Base Repository
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
void delete(ID id);
Iterable<T> findAll();
T findOne(ID id);
T save(T persisted);
Iterable<T> save(Iterable<T> persited);
}
Person Repository
public interface PersonRepository extends
BaseRepository<Person, Serializable> {
}
Is there a problem in my code?
Is there an issue with caching? Do I have to force flushing?
Is storing data with WebSockets supported by SpringBoot?
Do you know any examples with storing data? I could only find basic examples without storing data.
The problem was in my persistence configuration. I changed the configuration from a Java implementation to the application.properties file. I think there was a problem with my transaction manager.
To be complete, here is my current application.properties file:
spring.datasource.url = jdbc:postgresql://localhost:5432/test
spring.datasource.username = test
spring.datasource.password = test
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
I want to create a remote EJB for an entity class. Is there a way of implementing a method with one parameter beign an object of a class that specifically has the #Entity annotation? The purpose of this, is to create just one bean for all my entities.
Example:
public void save(Entity ent){
em.persist(ent);
}
If you just have 1 ejb for all entities you will soon run into problems when you have to handle some of them in a special way.
But you can do like this with an abstract super-ejb, and still have 1 ejb per entity, but its very easy to create. And you can still overwrite the default, inherited methods.
public abstract class AbstractEjb<T> {
private Class<T> entityClass;
public AbstractEjb(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0] + 1);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
To implement for an entity just do
#Stateless
public class TestEjb extends AbstractEjb<TestEntity> {
#PersistenceContext(unitName = "...")
private EntityManager em;
public TestEjb() {
super(TestEntity.class);
}
public EntityManager getEntityManager() {
return em;
}
}
Source: Generate JSF pages from entity classes in Netbeans.
I am using spring-data for mongodb with querydsl.
I have a repository
public interface DocumentRepository extends MongoRepository<Document, String> ,QueryDslPredicateExecutor<Document> {}
and an entity
#QueryEntity
public class Document {
private String id;
private String name;
private String description;
private boolean locked;
private String message;
}
I need to load a list of documents with id and name informations.
So only id and name should be loaded and set in my entity.
I think query projection is the right word for it.
Is this supported?
In addition I need to implement some lazy loading logic.
Is there anything like "skip" and "limit" features in a repository?
There's quite a few aspects to this, as it is - unfortunately - not a single question but multiple ones.
For the projection you can simply use the fields attribute of the #Query annotation:
interface DocumentRepository extends MongoRepository<Document, String>, QuerydslPredicateExecutor<Document> {
#Query(value = "{}", fields = "{ 'id' : 1, 'name' : 1 }")
List<Document> findDocumentsProjected();
}
You can combine this with the query derivation mechanism (by not setting query), with pagination (see below) and even a dedicated projection type in the return clause (e.g. a DocumentExcerpt with only id and name fields).
Pagination is fully supported on the repository abstraction. You already get findAll(Pageable) and a Querydsl specific version of the method by extending the base interfaces. You can also use the pagination API in finder methods adding a Pageable as parameter and returning a Page
Page<Document> findByDescriptionLike(String description, Pageable pageable)
See more on that in the reference documentation.
Projection
For all I know projections are not supported by the default Spring Data repositories. If you want to make sure only the projection is sent from the DB to your application (e.g. for performance reasons) you will have to implement the corresponding query yourself. Adding custom methods to extensions of the standard repo should not be too much effort.
If you just want to hide the content of certain fields from some client calling your application, you would typically use another set of entity objects with a suitable mapping in between. Using the same POJO for different levels of detail is always confusing as you will not know if a field is actually null or if the value was just suppressed in a certain context.
Pagination
I am currently not able to test any code, but according to the documentation of QueryDslPredicateExecutor the method findAll(predicate, pageable) should be what you want:
it returns a Page object that is a regular Iterable for your Document
you have to pass it a Pageable for which you can e.g. use a PageRequest; initializing it for known values of skip and limit should be trivial
I also found this approach for JPA
Spring Data JPA and Querydsl to fetch subset of columns using bean/constructor projection
I am currently trying to implement this for MongoDB.
According to the Answer of this -> Question <- I implemeted following solution.
Entity
#QueryEntity
public class Document extends AbstractObject {
}
Custom QuerydslMongoRepository
public interface CustomQuerydslMongoRepository<T extends AbstractObject,ID extends Serializable> extends MongoRepository<T, ID> ,QueryDslPredicateExecutor<T>{
Page<T> findAll(Predicate predicate, Pageable pageable,Path... paths);
Page<T> findAll(Predicate predicate, Pageable pageable,List<Path> projections);
}
Custom QuerydslMongoRepository Implementation
public class CustomQuerydslMongoRepositoryImpl<T extends AbstractObject,ID extends Serializable> extends QueryDslMongoRepository<T,ID> implements CustomQuerydslMongoRepository<T,ID> {
//All instance variables are available in super, but they are private
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> pathBuilder;
private final MongoOperations mongoOperations;
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) {
this(entityInformation, mongoOperations,DEFAULT_ENTITY_PATH_RESOLVER);
}
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations, EntityPathResolver resolver) {
super(entityInformation, mongoOperations, resolver);
this.path=resolver.createPath(entityInformation.getJavaType());
this.pathBuilder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.mongoOperations=mongoOperations;
}
#Override
public Page<T> findAll( Predicate predicate, Pageable pageable,Path... paths) {
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(mongoOperations, domainType);
long total = query.count();
List<T> content = total > pageable.getOffset() ? query.where(predicate).list(paths) : Collections.<T>emptyList();
return new PageImpl<T>(content, pageable, total);
}
#Override
public Page<T> findAll(Predicate predicate, Pageable pageable, List<Path> projections) {
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(mongoOperations, domainType);
long total = query.count();
List<T> content = total > pageable.getOffset() ? query.where(predicate).list(projections.toArray(new Path[0])) : Collections.<T>emptyList();
return new PageImpl<T>(content, pageable, total);
}
}
Custom Repository Factory
public class CustomQueryDslMongodbRepositoryFactoryBean<R extends QueryDslMongoRepository<T, I>, T, I extends Serializable> extends MongoRepositoryFactoryBean<R, T, I> {
#Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new CustomQueryDslMongodbRepositoryFactory<T,I>(operations);
}
public static class CustomQueryDslMongodbRepositoryFactory<T, I extends Serializable> extends MongoRepositoryFactory {
private MongoOperations operations;
public CustomQueryDslMongodbRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.operations = mongoOperations;
}
#SuppressWarnings({ "rawtypes", "unchecked" })
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CustomQuerydslMongoRepositoryImpl(getEntityInformation(metadata.getDomainType()), operations);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CustomQuerydslMongoRepository.class;
}
}
}
Entity Repository
public interface DocumentRepository extends CustomQuerydslMongoRepository<Document, String>{
}
Usage in Service
#Autowired
DocumentRepository repository;
public List<Document> getAllDocumentsForListing(){
return repository.findAll( QDocument.document.id.isNotEmpty().and(QDocument.document.version.isNotNull()), new PageRequest(0, 10),QDocument.document.name,QDocument.document.version).getContent();
}