In the case of CMP, you have a set of container-managed CMP fields. In ejbCreate() method, you populate those fields and generate the primary key and populate the CMP PK field with the generated value. Once you complete the method, the container looks into the CMP fields to get the values that you have populated and does the insert into the persistent store using those values. Now apart from just inserting the data in the persistent store, the container has to do a lot of other things for which it needs the primary key. For example, it has to create an EJB Object instance so that its stub can be returned to the client. But to create the EJB Object for the newly created entity, it needs the PK of the entity. Because EJBObject is something specific to each entity in the persistent store. Or you could say that EJBObject has a primaryKey property as it has a getPrimaryKey() method. Hence the container needs the PK to create the EJBObject. It also has to update the EntityContext of the bean instance representing the entity with the PK. (EntityContext also has a getPrimaryKey method). Now in CMP, the container can get the PK, just by looking at the CMP field value that you populated in the ejbCreate () method right? Hence there is no need for you to return the PK explicitly from the ejbCreate() method.
Now lets take the case of BMP. You do the insert in ejbCreate() , but the container still needs the primary key as before to create the EJBObject and update the EntityContext of the bean. How will the container know the PK? There are no container managed fields to look into. So the container will not know unless you tell it. Hence you have to return the PK to the container from the ejbCreate() method so that the container can go ahead and use it in creating the EJBObject etc
Hence ideally, for CMP, it should have been public void ejbCreate() and for BMP, public PK ejbCreate(). But the spec developers did not want to introduce separate interfaces for BMP and CMP. Even the EntityBean interface that both CMP and BMP classes implement is the same. Hence for consistency sake, they kept the method signature the same (both return PK), but you have to return null from the ejbCreate() method. I think even if you return the PK from ejbCreate() method of a CMP bean, the container will ignore it and always look into the CMP PK field to get the PK and do its job.